diff --git a/api/go1.24.txt b/api/go1.24.txt index 64ede33af25b7a..05e2006e0747bb 100644 --- a/api/go1.24.txt +++ b/api/go1.24.txt @@ -106,33 +106,26 @@ pkg debug/elf, const VER_FLG_INFO = 4 #63952 pkg debug/elf, const VER_FLG_INFO DynamicVersionFlag #63952 pkg debug/elf, const VER_FLG_WEAK = 2 #63952 pkg debug/elf, const VER_FLG_WEAK DynamicVersionFlag #63952 -pkg debug/elf, const VerFlagGlobal = 2 #63952 -pkg debug/elf, const VerFlagGlobal SymbolVersionFlag #63952 -pkg debug/elf, const VerFlagHidden = 4 #63952 -pkg debug/elf, const VerFlagHidden SymbolVersionFlag #63952 -pkg debug/elf, const VerFlagLocal = 1 #63952 -pkg debug/elf, const VerFlagLocal SymbolVersionFlag #63952 -pkg debug/elf, const VerFlagNone = 0 #63952 -pkg debug/elf, const VerFlagNone SymbolVersionFlag #63952 pkg debug/elf, method (*File) DynamicVersionNeeds() ([]DynamicVersionNeed, error) #63952 pkg debug/elf, method (*File) DynamicVersions() ([]DynamicVersion, error) #63952 pkg debug/elf, type DynamicVersion struct #63952 pkg debug/elf, type DynamicVersion struct, Deps []string #63952 pkg debug/elf, type DynamicVersion struct, Flags DynamicVersionFlag #63952 +pkg debug/elf, type DynamicVersion struct, Name string #63952 pkg debug/elf, type DynamicVersion struct, Index uint16 #63952 -pkg debug/elf, type DynamicVersion struct, Version uint16 #63952 pkg debug/elf, type DynamicVersionDep struct #63952 pkg debug/elf, type DynamicVersionDep struct, Dep string #63952 pkg debug/elf, type DynamicVersionDep struct, Flags DynamicVersionFlag #63952 -pkg debug/elf, type DynamicVersionDep struct, Other uint16 #63952 +pkg debug/elf, type DynamicVersionDep struct, Index uint16 #63952 pkg debug/elf, type DynamicVersionFlag uint16 #63952 pkg debug/elf, type DynamicVersionNeed struct #63952 pkg debug/elf, type DynamicVersionNeed struct, Name string #63952 pkg debug/elf, type DynamicVersionNeed struct, Needs []DynamicVersionDep #63952 -pkg debug/elf, type DynamicVersionNeed struct, Version uint16 #63952 -pkg debug/elf, type Symbol struct, VersionFlags SymbolVersionFlag #63952 -pkg debug/elf, type Symbol struct, VersionIndex int16 #63952 -pkg debug/elf, type SymbolVersionFlag uint8 #63952 +pkg debug/elf, type Symbol struct, HasVersion bool #63952 +pkg debug/elf, type Symbol struct, VersionIndex VersionIndex #63952 +pkg debug/elf, method (VersionIndex) Index() uint16 #63952 +pkg debug/elf, method (VersionIndex) IsHidden() bool #63952 +pkg debug/elf, type VersionIndex uint16 #63952 pkg encoding, type BinaryAppender interface { AppendBinary } #62384 pkg encoding, type BinaryAppender interface, AppendBinary([]uint8) ([]uint8, error) #62384 pkg encoding, type TextAppender interface { AppendText } #62384 diff --git a/api/next/49580.txt b/api/next/49580.txt new file mode 100644 index 00000000000000..ce213cc9ca6816 --- /dev/null +++ b/api/next/49580.txt @@ -0,0 +1,8 @@ +pkg io/fs, func Lstat(FS, string) (FileInfo, error) #49580 +pkg io/fs, func ReadLink(FS, string) (string, error) #49580 +pkg io/fs, type ReadLinkFS interface { Lstat, Open, ReadLink } #49580 +pkg io/fs, type ReadLinkFS interface, Lstat(string) (FileInfo, error) #49580 +pkg io/fs, type ReadLinkFS interface, Open(string) (File, error) #49580 +pkg io/fs, type ReadLinkFS interface, ReadLink(string) (string, error) #49580 +pkg testing/fstest, method (MapFS) Lstat(string) (fs.FileInfo, error) #49580 +pkg testing/fstest, method (MapFS) ReadLink(string) (string, error) #49580 diff --git a/api/next/67002.txt b/api/next/67002.txt new file mode 100644 index 00000000000000..216d3a3afeb606 --- /dev/null +++ b/api/next/67002.txt @@ -0,0 +1,2 @@ +pkg os, method (*Root) Chmod(string, fs.FileMode) error #67002 +pkg os, method (*Root) Chown(string, int, int) error #67002 diff --git a/doc/go1.17_spec.html b/doc/go1.17_spec.html deleted file mode 100644 index dbff3598b5838b..00000000000000 --- a/doc/go1.17_spec.html +++ /dev/null @@ -1,6864 +0,0 @@ - - -

Introduction

- -

-This is the reference manual for the Go programming language as it was for -language version 1.17, in October 2021, before the introduction of generics. -It is provided for historical interest. -The current reference manual can be found here. -For more information and other documents, see go.dev. -

- -

-Go is a general-purpose language designed with systems programming -in mind. It is strongly typed and garbage-collected and has explicit -support for concurrent programming. Programs are constructed from -packages, whose properties allow efficient management of -dependencies. -

- -

-The grammar is compact and simple to parse, allowing for easy analysis -by automatic tools such as integrated development environments. -

- -

Notation

-

-The syntax is specified using Extended Backus-Naur Form (EBNF): -

- -
-Production  = production_name "=" [ Expression ] "." .
-Expression  = Alternative { "|" Alternative } .
-Alternative = Term { Term } .
-Term        = production_name | token [ "…" token ] | Group | Option | Repetition .
-Group       = "(" Expression ")" .
-Option      = "[" Expression "]" .
-Repetition  = "{" Expression "}" .
-
- -

-Productions are expressions constructed from terms and the following -operators, in increasing precedence: -

-
-|   alternation
-()  grouping
-[]  option (0 or 1 times)
-{}  repetition (0 to n times)
-
- -

-Lower-case production names are used to identify lexical tokens. -Non-terminals are in CamelCase. Lexical tokens are enclosed in -double quotes "" or back quotes ``. -

- -

-The form a … b represents the set of characters from -a through b as alternatives. The horizontal -ellipsis is also used elsewhere in the spec to informally denote various -enumerations or code snippets that are not further specified. The character -(as opposed to the three characters ...) is not a token of the Go -language. -

- -

Source code representation

- -

-Source code is Unicode text encoded in -UTF-8. The text is not -canonicalized, so a single accented code point is distinct from the -same character constructed from combining an accent and a letter; -those are treated as two code points. For simplicity, this document -will use the unqualified term character to refer to a Unicode code point -in the source text. -

-

-Each code point is distinct; for instance, upper and lower case letters -are different characters. -

-

-Implementation restriction: For compatibility with other tools, a -compiler may disallow the NUL character (U+0000) in the source text. -

-

-Implementation restriction: For compatibility with other tools, a -compiler may ignore a UTF-8-encoded byte order mark -(U+FEFF) if it is the first Unicode code point in the source text. -A byte order mark may be disallowed anywhere else in the source. -

- -

Characters

- -

-The following terms are used to denote specific Unicode character classes: -

-
-newline        = /* the Unicode code point U+000A */ .
-unicode_char   = /* an arbitrary Unicode code point except newline */ .
-unicode_letter = /* a Unicode code point classified as "Letter" */ .
-unicode_digit  = /* a Unicode code point classified as "Number, decimal digit" */ .
-
- -

-In The Unicode Standard 8.0, -Section 4.5 "General Category" defines a set of character categories. -Go treats all characters in any of the Letter categories Lu, Ll, Lt, Lm, or Lo -as Unicode letters, and those in the Number category Nd as Unicode digits. -

- -

Letters and digits

- -

-The underscore character _ (U+005F) is considered a letter. -

-
-letter        = unicode_letter | "_" .
-decimal_digit = "0" … "9" .
-binary_digit  = "0" | "1" .
-octal_digit   = "0" … "7" .
-hex_digit     = "0" … "9" | "A" … "F" | "a" … "f" .
-
- -

Lexical elements

- -

Comments

- -

-Comments serve as program documentation. There are two forms: -

- -
    -
  1. -Line comments start with the character sequence // -and stop at the end of the line. -
  2. -
  3. -General comments start with the character sequence /* -and stop with the first subsequent character sequence */. -
  4. -
- -

-A comment cannot start inside a rune or -string literal, or inside a comment. -A general comment containing no newlines acts like a space. -Any other comment acts like a newline. -

- -

Tokens

- -

-Tokens form the vocabulary of the Go language. -There are four classes: identifiers, keywords, operators -and punctuation, and literals. White space, formed from -spaces (U+0020), horizontal tabs (U+0009), -carriage returns (U+000D), and newlines (U+000A), -is ignored except as it separates tokens -that would otherwise combine into a single token. Also, a newline or end of file -may trigger the insertion of a semicolon. -While breaking the input into tokens, -the next token is the longest sequence of characters that form a -valid token. -

- -

Semicolons

- -

-The formal grammar uses semicolons ";" as terminators in -a number of productions. Go programs may omit most of these semicolons -using the following two rules: -

- -
    -
  1. -When the input is broken into tokens, a semicolon is automatically inserted -into the token stream immediately after a line's final token if that token is - -
  2. - -
  3. -To allow complex statements to occupy a single line, a semicolon -may be omitted before a closing ")" or "}". -
  4. -
- -

-To reflect idiomatic use, code examples in this document elide semicolons -using these rules. -

- - -

Identifiers

- -

-Identifiers name program entities such as variables and types. -An identifier is a sequence of one or more letters and digits. -The first character in an identifier must be a letter. -

-
-identifier = letter { letter | unicode_digit } .
-
-
-a
-_x9
-ThisVariableIsExported
-αβ
-
- -

-Some identifiers are predeclared. -

- - -

Keywords

- -

-The following keywords are reserved and may not be used as identifiers. -

-
-break        default      func         interface    select
-case         defer        go           map          struct
-chan         else         goto         package      switch
-const        fallthrough  if           range        type
-continue     for          import       return       var
-
- -

Operators and punctuation

- -

-The following character sequences represent operators -(including assignment operators) and punctuation: -

-
-+    &     +=    &=     &&    ==    !=    (    )
--    |     -=    |=     ||    <     <=    [    ]
-*    ^     *=    ^=     <-    >     >=    {    }
-/    <<    /=    <<=    ++    =     :=    ,    ;
-%    >>    %=    >>=    --    !     ...   .    :
-     &^          &^=
-
- -

Integer literals

- -

-An integer literal is a sequence of digits representing an -integer constant. -An optional prefix sets a non-decimal base: 0b or 0B -for binary, 0, 0o, or 0O for octal, -and 0x or 0X for hexadecimal. -A single 0 is considered a decimal zero. -In hexadecimal literals, letters a through f -and A through F represent values 10 through 15. -

- -

-For readability, an underscore character _ may appear after -a base prefix or between successive digits; such underscores do not change -the literal's value. -

-
-int_lit        = decimal_lit | binary_lit | octal_lit | hex_lit .
-decimal_lit    = "0" | ( "1" … "9" ) [ [ "_" ] decimal_digits ] .
-binary_lit     = "0" ( "b" | "B" ) [ "_" ] binary_digits .
-octal_lit      = "0" [ "o" | "O" ] [ "_" ] octal_digits .
-hex_lit        = "0" ( "x" | "X" ) [ "_" ] hex_digits .
-
-decimal_digits = decimal_digit { [ "_" ] decimal_digit } .
-binary_digits  = binary_digit { [ "_" ] binary_digit } .
-octal_digits   = octal_digit { [ "_" ] octal_digit } .
-hex_digits     = hex_digit { [ "_" ] hex_digit } .
-
- -
-42
-4_2
-0600
-0_600
-0o600
-0O600       // second character is capital letter 'O'
-0xBadFace
-0xBad_Face
-0x_67_7a_2f_cc_40_c6
-170141183460469231731687303715884105727
-170_141183_460469_231731_687303_715884_105727
-
-_42         // an identifier, not an integer literal
-42_         // invalid: _ must separate successive digits
-4__2        // invalid: only one _ at a time
-0_xBadFace  // invalid: _ must separate successive digits
-
- - -

Floating-point literals

- -

-A floating-point literal is a decimal or hexadecimal representation of a -floating-point constant. -

- -

-A decimal floating-point literal consists of an integer part (decimal digits), -a decimal point, a fractional part (decimal digits), and an exponent part -(e or E followed by an optional sign and decimal digits). -One of the integer part or the fractional part may be elided; one of the decimal point -or the exponent part may be elided. -An exponent value exp scales the mantissa (integer and fractional part) by 10exp. -

- -

-A hexadecimal floating-point literal consists of a 0x or 0X -prefix, an integer part (hexadecimal digits), a radix point, a fractional part (hexadecimal digits), -and an exponent part (p or P followed by an optional sign and decimal digits). -One of the integer part or the fractional part may be elided; the radix point may be elided as well, -but the exponent part is required. (This syntax matches the one given in IEEE 754-2008 §5.12.3.) -An exponent value exp scales the mantissa (integer and fractional part) by 2exp. -

- -

-For readability, an underscore character _ may appear after -a base prefix or between successive digits; such underscores do not change -the literal value. -

- -
-float_lit         = decimal_float_lit | hex_float_lit .
-
-decimal_float_lit = decimal_digits "." [ decimal_digits ] [ decimal_exponent ] |
-                    decimal_digits decimal_exponent |
-                    "." decimal_digits [ decimal_exponent ] .
-decimal_exponent  = ( "e" | "E" ) [ "+" | "-" ] decimal_digits .
-
-hex_float_lit     = "0" ( "x" | "X" ) hex_mantissa hex_exponent .
-hex_mantissa      = [ "_" ] hex_digits "." [ hex_digits ] |
-                    [ "_" ] hex_digits |
-                    "." hex_digits .
-hex_exponent      = ( "p" | "P" ) [ "+" | "-" ] decimal_digits .
-
- -
-0.
-72.40
-072.40       // == 72.40
-2.71828
-1.e+0
-6.67428e-11
-1E6
-.25
-.12345E+5
-1_5.         // == 15.0
-0.15e+0_2    // == 15.0
-
-0x1p-2       // == 0.25
-0x2.p10      // == 2048.0
-0x1.Fp+0     // == 1.9375
-0X.8p-0      // == 0.5
-0X_1FFFP-16  // == 0.1249847412109375
-0x15e-2      // == 0x15e - 2 (integer subtraction)
-
-0x.p1        // invalid: mantissa has no digits
-1p-2         // invalid: p exponent requires hexadecimal mantissa
-0x1.5e-2     // invalid: hexadecimal mantissa requires p exponent
-1_.5         // invalid: _ must separate successive digits
-1._5         // invalid: _ must separate successive digits
-1.5_e1       // invalid: _ must separate successive digits
-1.5e_1       // invalid: _ must separate successive digits
-1.5e1_       // invalid: _ must separate successive digits
-
- - -

Imaginary literals

- -

-An imaginary literal represents the imaginary part of a -complex constant. -It consists of an integer or -floating-point literal -followed by the lower-case letter i. -The value of an imaginary literal is the value of the respective -integer or floating-point literal multiplied by the imaginary unit i. -

- -
-imaginary_lit = (decimal_digits | int_lit | float_lit) "i" .
-
- -

-For backward compatibility, an imaginary literal's integer part consisting -entirely of decimal digits (and possibly underscores) is considered a decimal -integer, even if it starts with a leading 0. -

- -
-0i
-0123i         // == 123i for backward-compatibility
-0o123i        // == 0o123 * 1i == 83i
-0xabci        // == 0xabc * 1i == 2748i
-0.i
-2.71828i
-1.e+0i
-6.67428e-11i
-1E6i
-.25i
-.12345E+5i
-0x1p-2i       // == 0x1p-2 * 1i == 0.25i
-
- - -

Rune literals

- -

-A rune literal represents a rune constant, -an integer value identifying a Unicode code point. -A rune literal is expressed as one or more characters enclosed in single quotes, -as in 'x' or '\n'. -Within the quotes, any character may appear except newline and unescaped single -quote. A single quoted character represents the Unicode value -of the character itself, -while multi-character sequences beginning with a backslash encode -values in various formats. -

- -

-The simplest form represents the single character within the quotes; -since Go source text is Unicode characters encoded in UTF-8, multiple -UTF-8-encoded bytes may represent a single integer value. For -instance, the literal 'a' holds a single byte representing -a literal a, Unicode U+0061, value 0x61, while -'ä' holds two bytes (0xc3 0xa4) representing -a literal a-dieresis, U+00E4, value 0xe4. -

- -

-Several backslash escapes allow arbitrary values to be encoded as -ASCII text. There are four ways to represent the integer value -as a numeric constant: \x followed by exactly two hexadecimal -digits; \u followed by exactly four hexadecimal digits; -\U followed by exactly eight hexadecimal digits, and a -plain backslash \ followed by exactly three octal digits. -In each case the value of the literal is the value represented by -the digits in the corresponding base. -

- -

-Although these representations all result in an integer, they have -different valid ranges. Octal escapes must represent a value between -0 and 255 inclusive. Hexadecimal escapes satisfy this condition -by construction. The escapes \u and \U -represent Unicode code points so within them some values are illegal, -in particular those above 0x10FFFF and surrogate halves. -

- -

-After a backslash, certain single-character escapes represent special values: -

- -
-\a   U+0007 alert or bell
-\b   U+0008 backspace
-\f   U+000C form feed
-\n   U+000A line feed or newline
-\r   U+000D carriage return
-\t   U+0009 horizontal tab
-\v   U+000B vertical tab
-\\   U+005C backslash
-\'   U+0027 single quote  (valid escape only within rune literals)
-\"   U+0022 double quote  (valid escape only within string literals)
-
- -

-All other sequences starting with a backslash are illegal inside rune literals. -

-
-rune_lit         = "'" ( unicode_value | byte_value ) "'" .
-unicode_value    = unicode_char | little_u_value | big_u_value | escaped_char .
-byte_value       = octal_byte_value | hex_byte_value .
-octal_byte_value = `\` octal_digit octal_digit octal_digit .
-hex_byte_value   = `\` "x" hex_digit hex_digit .
-little_u_value   = `\` "u" hex_digit hex_digit hex_digit hex_digit .
-big_u_value      = `\` "U" hex_digit hex_digit hex_digit hex_digit
-                           hex_digit hex_digit hex_digit hex_digit .
-escaped_char     = `\` ( "a" | "b" | "f" | "n" | "r" | "t" | "v" | `\` | "'" | `"` ) .
-
- -
-'a'
-'ä'
-'本'
-'\t'
-'\000'
-'\007'
-'\377'
-'\x07'
-'\xff'
-'\u12e4'
-'\U00101234'
-'\''         // rune literal containing single quote character
-'aa'         // illegal: too many characters
-'\xa'        // illegal: too few hexadecimal digits
-'\0'         // illegal: too few octal digits
-'\uDFFF'     // illegal: surrogate half
-'\U00110000' // illegal: invalid Unicode code point
-
- - -

String literals

- -

-A string literal represents a string constant -obtained from concatenating a sequence of characters. There are two forms: -raw string literals and interpreted string literals. -

- -

-Raw string literals are character sequences between back quotes, as in -`foo`. Within the quotes, any character may appear except -back quote. The value of a raw string literal is the -string composed of the uninterpreted (implicitly UTF-8-encoded) characters -between the quotes; -in particular, backslashes have no special meaning and the string may -contain newlines. -Carriage return characters ('\r') inside raw string literals -are discarded from the raw string value. -

- -

-Interpreted string literals are character sequences between double -quotes, as in "bar". -Within the quotes, any character may appear except newline and unescaped double quote. -The text between the quotes forms the -value of the literal, with backslash escapes interpreted as they -are in rune literals (except that \' is illegal and -\" is legal), with the same restrictions. -The three-digit octal (\nnn) -and two-digit hexadecimal (\xnn) escapes represent individual -bytes of the resulting string; all other escapes represent -the (possibly multi-byte) UTF-8 encoding of individual characters. -Thus inside a string literal \377 and \xFF represent -a single byte of value 0xFF=255, while ÿ, -\u00FF, \U000000FF and \xc3\xbf represent -the two bytes 0xc3 0xbf of the UTF-8 encoding of character -U+00FF. -

- -
-string_lit             = raw_string_lit | interpreted_string_lit .
-raw_string_lit         = "`" { unicode_char | newline } "`" .
-interpreted_string_lit = `"` { unicode_value | byte_value } `"` .
-
- -
-`abc`                // same as "abc"
-`\n
-\n`                  // same as "\\n\n\\n"
-"\n"
-"\""                 // same as `"`
-"Hello, world!\n"
-"日本語"
-"\u65e5本\U00008a9e"
-"\xff\u00FF"
-"\uD800"             // illegal: surrogate half
-"\U00110000"         // illegal: invalid Unicode code point
-
- -

-These examples all represent the same string: -

- -
-"日本語"                                 // UTF-8 input text
-`日本語`                                 // UTF-8 input text as a raw literal
-"\u65e5\u672c\u8a9e"                    // the explicit Unicode code points
-"\U000065e5\U0000672c\U00008a9e"        // the explicit Unicode code points
-"\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"  // the explicit UTF-8 bytes
-
- -

-If the source code represents a character as two code points, such as -a combining form involving an accent and a letter, the result will be -an error if placed in a rune literal (it is not a single code -point), and will appear as two code points if placed in a string -literal. -

- - -

Constants

- -

There are boolean constants, -rune constants, -integer constants, -floating-point constants, complex constants, -and string constants. Rune, integer, floating-point, -and complex constants are -collectively called numeric constants. -

- -

-A constant value is represented by a -rune, -integer, -floating-point, -imaginary, -or -string literal, -an identifier denoting a constant, -a constant expression, -a conversion with a result that is a constant, or -the result value of some built-in functions such as -unsafe.Sizeof applied to any value, -cap or len applied to -some expressions, -real and imag applied to a complex constant -and complex applied to numeric constants. -The boolean truth values are represented by the predeclared constants -true and false. The predeclared identifier -iota denotes an integer constant. -

- -

-In general, complex constants are a form of -constant expression -and are discussed in that section. -

- -

-Numeric constants represent exact values of arbitrary precision and do not overflow. -Consequently, there are no constants denoting the IEEE 754 negative zero, infinity, -and not-a-number values. -

- -

-Constants may be typed or untyped. -Literal constants, true, false, iota, -and certain constant expressions -containing only untyped constant operands are untyped. -

- -

-A constant may be given a type explicitly by a constant declaration -or conversion, or implicitly when used in a -variable declaration or an -assignment or as an -operand in an expression. -It is an error if the constant value -cannot be represented as a value of the respective type. -

- -

-An untyped constant has a default type which is the type to which the -constant is implicitly converted in contexts where a typed value is required, -for instance, in a short variable declaration -such as i := 0 where there is no explicit type. -The default type of an untyped constant is bool, rune, -int, float64, complex128 or string -respectively, depending on whether it is a boolean, rune, integer, floating-point, -complex, or string constant. -

- -

-Implementation restriction: Although numeric constants have arbitrary -precision in the language, a compiler may implement them using an -internal representation with limited precision. That said, every -implementation must: -

- - - -

-These requirements apply both to literal constants and to the result -of evaluating constant -expressions. -

- - -

Variables

- -

-A variable is a storage location for holding a value. -The set of permissible values is determined by the -variable's type. -

- -

-A variable declaration -or, for function parameters and results, the signature -of a function declaration -or function literal reserves -storage for a named variable. - -Calling the built-in function new -or taking the address of a composite literal -allocates storage for a variable at run time. -Such an anonymous variable is referred to via a (possibly implicit) -pointer indirection. -

- -

-Structured variables of array, slice, -and struct types have elements and fields that may -be addressed individually. Each such element -acts like a variable. -

- -

-The static type (or just type) of a variable is the -type given in its declaration, the type provided in the -new call or composite literal, or the type of -an element of a structured variable. -Variables of interface type also have a distinct dynamic type, -which is the concrete type of the value assigned to the variable at run time -(unless the value is the predeclared identifier nil, -which has no type). -The dynamic type may vary during execution but values stored in interface -variables are always assignable -to the static type of the variable. -

- -
-var x interface{}  // x is nil and has static type interface{}
-var v *T           // v has value nil, static type *T
-x = 42             // x has value 42 and dynamic type int
-x = v              // x has value (*T)(nil) and dynamic type *T
-
- -

-A variable's value is retrieved by referring to the variable in an -expression; it is the most recent value -assigned to the variable. -If a variable has not yet been assigned a value, its value is the -zero value for its type. -

- - -

Types

- -

-A type determines a set of values together with operations and methods specific -to those values. A type may be denoted by a type name, if it has one, -or specified using a type literal, which composes a type from existing types. -

- -
-Type      = TypeName | TypeLit | "(" Type ")" .
-TypeName  = identifier | QualifiedIdent .
-TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
-	    SliceType | MapType | ChannelType .
-
- -

-The language predeclares certain type names. -Others are introduced with type declarations. -Composite types—array, struct, pointer, function, -interface, slice, map, and channel types—may be constructed using -type literals. -

- -

-Each type T has an underlying type: If T -is one of the predeclared boolean, numeric, or string types, or a type literal, -the corresponding underlying -type is T itself. Otherwise, T's underlying type -is the underlying type of the type to which T refers in its -type declaration. -

- -
-type (
-	A1 = string
-	A2 = A1
-)
-
-type (
-	B1 string
-	B2 B1
-	B3 []B1
-	B4 B3
-)
-
- -

-The underlying type of string, A1, A2, B1, -and B2 is string. -The underlying type of []B1, B3, and B4 is []B1. -

- -

Method sets

-

-A type has a (possibly empty) method set associated with it. -The method set of an interface type is its interface. -The method set of any other type T consists of all -methods declared with receiver type T. -The method set of the corresponding pointer type *T -is the set of all methods declared with receiver *T or T -(that is, it also contains the method set of T). -Further rules apply to structs containing embedded fields, as described -in the section on struct types. -Any other type has an empty method set. -In a method set, each method must have a -unique -non-blank method name. -

- -

-The method set of a type determines the interfaces that the -type implements -and the methods that can be called -using a receiver of that type. -

- -

Boolean types

- -

-A boolean type represents the set of Boolean truth values -denoted by the predeclared constants true -and false. The predeclared boolean type is bool; -it is a defined type. -

- -

Numeric types

- -

-A numeric type represents sets of integer or floating-point values. -The predeclared architecture-independent numeric types are: -

- -
-uint8       the set of all unsigned  8-bit integers (0 to 255)
-uint16      the set of all unsigned 16-bit integers (0 to 65535)
-uint32      the set of all unsigned 32-bit integers (0 to 4294967295)
-uint64      the set of all unsigned 64-bit integers (0 to 18446744073709551615)
-
-int8        the set of all signed  8-bit integers (-128 to 127)
-int16       the set of all signed 16-bit integers (-32768 to 32767)
-int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
-int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)
-
-float32     the set of all IEEE 754 32-bit floating-point numbers
-float64     the set of all IEEE 754 64-bit floating-point numbers
-
-complex64   the set of all complex numbers with float32 real and imaginary parts
-complex128  the set of all complex numbers with float64 real and imaginary parts
-
-byte        alias for uint8
-rune        alias for int32
-
- -

-The value of an n-bit integer is n bits wide and represented using -two's complement arithmetic. -

- -

-There is also a set of predeclared numeric types with implementation-specific sizes: -

- -
-uint     either 32 or 64 bits
-int      same size as uint
-uintptr  an unsigned integer large enough to store the uninterpreted bits of a pointer value
-
- -

-To avoid portability issues all numeric types are defined -types and thus distinct except -byte, which is an alias for uint8, and -rune, which is an alias for int32. -Explicit conversions -are required when different numeric types are mixed in an expression -or assignment. For instance, int32 and int -are not the same type even though they may have the same size on a -particular architecture. -

- -

String types

- -

-A string type represents the set of string values. -A string value is a (possibly empty) sequence of bytes. -The number of bytes is called the length of the string and is never negative. -Strings are immutable: once created, -it is impossible to change the contents of a string. -The predeclared string type is string; -it is a defined type. -

- -

-The length of a string s can be discovered using -the built-in function len. -The length is a compile-time constant if the string is a constant. -A string's bytes can be accessed by integer indices -0 through len(s)-1. -It is illegal to take the address of such an element; if -s[i] is the i'th byte of a -string, &s[i] is invalid. -

- - -

Array types

- -

-An array is a numbered sequence of elements of a single -type, called the element type. -The number of elements is called the length of the array and is never negative. -

- -
-ArrayType   = "[" ArrayLength "]" ElementType .
-ArrayLength = Expression .
-ElementType = Type .
-
- -

-The length is part of the array's type; it must evaluate to a -non-negative constant -representable by a value -of type int. -The length of array a can be discovered -using the built-in function len. -The elements can be addressed by integer indices -0 through len(a)-1. -Array types are always one-dimensional but may be composed to form -multi-dimensional types. -

- -
-[32]byte
-[2*N] struct { x, y int32 }
-[1000]*float64
-[3][5]int
-[2][2][2]float64  // same as [2]([2]([2]float64))
-
- -

Slice types

- -

-A slice is a descriptor for a contiguous segment of an underlying array and -provides access to a numbered sequence of elements from that array. -A slice type denotes the set of all slices of arrays of its element type. -The number of elements is called the length of the slice and is never negative. -The value of an uninitialized slice is nil. -

- -
-SliceType = "[" "]" ElementType .
-
- -

-The length of a slice s can be discovered by the built-in function -len; unlike with arrays it may change during -execution. The elements can be addressed by integer indices -0 through len(s)-1. The slice index of a -given element may be less than the index of the same element in the -underlying array. -

-

-A slice, once initialized, is always associated with an underlying -array that holds its elements. A slice therefore shares storage -with its array and with other slices of the same array; by contrast, -distinct arrays always represent distinct storage. -

-

-The array underlying a slice may extend past the end of the slice. -The capacity is a measure of that extent: it is the sum of -the length of the slice and the length of the array beyond the slice; -a slice of length up to that capacity can be created by -slicing a new one from the original slice. -The capacity of a slice a can be discovered using the -built-in function cap(a). -

- -

-A new, initialized slice value for a given element type T is -made using the built-in function -make, -which takes a slice type -and parameters specifying the length and optionally the capacity. -A slice created with make always allocates a new, hidden array -to which the returned slice value refers. That is, executing -

- -
-make([]T, length, capacity)
-
- -

-produces the same slice as allocating an array and slicing -it, so these two expressions are equivalent: -

- -
-make([]int, 50, 100)
-new([100]int)[0:50]
-
- -

-Like arrays, slices are always one-dimensional but may be composed to construct -higher-dimensional objects. -With arrays of arrays, the inner arrays are, by construction, always the same length; -however with slices of slices (or arrays of slices), the inner lengths may vary dynamically. -Moreover, the inner slices must be initialized individually. -

- -

Struct types

- -

-A struct is a sequence of named elements, called fields, each of which has a -name and a type. Field names may be specified explicitly (IdentifierList) or -implicitly (EmbeddedField). -Within a struct, non-blank field names must -be unique. -

- -
-StructType    = "struct" "{" { FieldDecl ";" } "}" .
-FieldDecl     = (IdentifierList Type | EmbeddedField) [ Tag ] .
-EmbeddedField = [ "*" ] TypeName .
-Tag           = string_lit .
-
- -
-// An empty struct.
-struct {}
-
-// A struct with 6 fields.
-struct {
-	x, y int
-	u float32
-	_ float32  // padding
-	A *[]int
-	F func()
-}
-
- -

-A field declared with a type but no explicit field name is called an embedded field. -An embedded field must be specified as -a type name T or as a pointer to a non-interface type name *T, -and T itself may not be -a pointer type. The unqualified type name acts as the field name. -

- -
-// A struct with four embedded fields of types T1, *T2, P.T3 and *P.T4
-struct {
-	T1        // field name is T1
-	*T2       // field name is T2
-	P.T3      // field name is T3
-	*P.T4     // field name is T4
-	x, y int  // field names are x and y
-}
-
- -

-The following declaration is illegal because field names must be unique -in a struct type: -

- -
-struct {
-	T     // conflicts with embedded field *T and *P.T
-	*T    // conflicts with embedded field T and *P.T
-	*P.T  // conflicts with embedded field T and *T
-}
-
- -

-A field or method f of an -embedded field in a struct x is called promoted if -x.f is a legal selector that denotes -that field or method f. -

- -

-Promoted fields act like ordinary fields -of a struct except that they cannot be used as field names in -composite literals of the struct. -

- -

-Given a struct type S and a defined type -T, promoted methods are included in the method set of the struct as follows: -

- - -

-A field declaration may be followed by an optional string literal tag, -which becomes an attribute for all the fields in the corresponding -field declaration. An empty tag string is equivalent to an absent tag. -The tags are made visible through a reflection interface -and take part in type identity for structs -but are otherwise ignored. -

- -
-struct {
-	x, y float64 ""  // an empty tag string is like an absent tag
-	name string  "any string is permitted as a tag"
-	_    [4]byte "ceci n'est pas un champ de structure"
-}
-
-// A struct corresponding to a TimeStamp protocol buffer.
-// The tag strings define the protocol buffer field numbers;
-// they follow the convention outlined by the reflect package.
-struct {
-	microsec  uint64 `protobuf:"1"`
-	serverIP6 uint64 `protobuf:"2"`
-}
-
- -

Pointer types

- -

-A pointer type denotes the set of all pointers to variables of a given -type, called the base type of the pointer. -The value of an uninitialized pointer is nil. -

- -
-PointerType = "*" BaseType .
-BaseType    = Type .
-
- -
-*Point
-*[4]int
-
- -

Function types

- -

-A function type denotes the set of all functions with the same parameter -and result types. The value of an uninitialized variable of function type -is nil. -

- -
-FunctionType   = "func" Signature .
-Signature      = Parameters [ Result ] .
-Result         = Parameters | Type .
-Parameters     = "(" [ ParameterList [ "," ] ] ")" .
-ParameterList  = ParameterDecl { "," ParameterDecl } .
-ParameterDecl  = [ IdentifierList ] [ "..." ] Type .
-
- -

-Within a list of parameters or results, the names (IdentifierList) -must either all be present or all be absent. If present, each name -stands for one item (parameter or result) of the specified type and -all non-blank names in the signature -must be unique. -If absent, each type stands for one item of that type. -Parameter and result -lists are always parenthesized except that if there is exactly -one unnamed result it may be written as an unparenthesized type. -

- -

-The final incoming parameter in a function signature may have -a type prefixed with .... -A function with such a parameter is called variadic and -may be invoked with zero or more arguments for that parameter. -

- -
-func()
-func(x int) int
-func(a, _ int, z float32) bool
-func(a, b int, z float32) (bool)
-func(prefix string, values ...int)
-func(a, b int, z float64, opt ...interface{}) (success bool)
-func(int, int, float64) (float64, *[]int)
-func(n int) func(p *T)
-
- - -

Interface types

- -

-An interface type specifies a method set called its interface. -A variable of interface type can store a value of any type with a method set -that is any superset of the interface. Such a type is said to -implement the interface. -The value of an uninitialized variable of interface type is nil. -

- -
-InterfaceType      = "interface" "{" { ( MethodSpec | InterfaceTypeName ) ";" } "}" .
-MethodSpec         = MethodName Signature .
-MethodName         = identifier .
-InterfaceTypeName  = TypeName .
-
- -

-An interface type may specify methods explicitly through method specifications, -or it may embed methods of other interfaces through interface type names. -

- -
-// A simple File interface.
-interface {
-	Read([]byte) (int, error)
-	Write([]byte) (int, error)
-	Close() error
-}
-
- -

-The name of each explicitly specified method must be unique -and not blank. -

- -
-interface {
-	String() string
-	String() string  // illegal: String not unique
-	_(x int)         // illegal: method must have non-blank name
-}
-
- -

-More than one type may implement an interface. -For instance, if two types S1 and S2 -have the method set -

- -
-func (p T) Read(p []byte) (n int, err error)
-func (p T) Write(p []byte) (n int, err error)
-func (p T) Close() error
-
- -

-(where T stands for either S1 or S2) -then the File interface is implemented by both S1 and -S2, regardless of what other methods -S1 and S2 may have or share. -

- -

-A type implements any interface comprising any subset of its methods -and may therefore implement several distinct interfaces. For -instance, all types implement the empty interface: -

- -
-interface{}
-
- -

-Similarly, consider this interface specification, -which appears within a type declaration -to define an interface called Locker: -

- -
-type Locker interface {
-	Lock()
-	Unlock()
-}
-
- -

-If S1 and S2 also implement -

- -
-func (p T) Lock() { … }
-func (p T) Unlock() { … }
-
- -

-they implement the Locker interface as well -as the File interface. -

- -

-An interface T may use a (possibly qualified) interface type -name E in place of a method specification. This is called -embedding interface E in T. -The method set of T is the union -of the method sets of T’s explicitly declared methods and of -T’s embedded interfaces. -

- -
-type Reader interface {
-	Read(p []byte) (n int, err error)
-	Close() error
-}
-
-type Writer interface {
-	Write(p []byte) (n int, err error)
-	Close() error
-}
-
-// ReadWriter's methods are Read, Write, and Close.
-type ReadWriter interface {
-	Reader  // includes methods of Reader in ReadWriter's method set
-	Writer  // includes methods of Writer in ReadWriter's method set
-}
-
- -

-A union of method sets contains the (exported and non-exported) -methods of each method set exactly once, and methods with the -same names must -have identical signatures. -

- -
-type ReadCloser interface {
-	Reader   // includes methods of Reader in ReadCloser's method set
-	Close()  // illegal: signatures of Reader.Close and Close are different
-}
-
- -

-An interface type T may not embed itself -or any interface type that embeds T, recursively. -

- -
-// illegal: Bad cannot embed itself
-type Bad interface {
-	Bad
-}
-
-// illegal: Bad1 cannot embed itself using Bad2
-type Bad1 interface {
-	Bad2
-}
-type Bad2 interface {
-	Bad1
-}
-
- -

Map types

- -

-A map is an unordered group of elements of one type, called the -element type, indexed by a set of unique keys of another type, -called the key type. -The value of an uninitialized map is nil. -

- -
-MapType     = "map" "[" KeyType "]" ElementType .
-KeyType     = Type .
-
- -

-The comparison operators -== and != must be fully defined -for operands of the key type; thus the key type must not be a function, map, or -slice. -If the key type is an interface type, these -comparison operators must be defined for the dynamic key values; -failure will cause a run-time panic. - -

- -
-map[string]int
-map[*T]struct{ x, y float64 }
-map[string]interface{}
-
- -

-The number of map elements is called its length. -For a map m, it can be discovered using the -built-in function len -and may change during execution. Elements may be added during execution -using assignments and retrieved with -index expressions; they may be removed with the -delete built-in function. -

-

-A new, empty map value is made using the built-in -function make, -which takes the map type and an optional capacity hint as arguments: -

- -
-make(map[string]int)
-make(map[string]int, 100)
-
- -

-The initial capacity does not bound its size: -maps grow to accommodate the number of items -stored in them, with the exception of nil maps. -A nil map is equivalent to an empty map except that no elements -may be added. -

- -

Channel types

- -

-A channel provides a mechanism for -concurrently executing functions -to communicate by -sending and -receiving -values of a specified element type. -The value of an uninitialized channel is nil. -

- -
-ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .
-
- -

-The optional <- operator specifies the channel direction, -send or receive. If no direction is given, the channel is -bidirectional. -A channel may be constrained only to send or only to receive by -assignment or -explicit conversion. -

- -
-chan T          // can be used to send and receive values of type T
-chan<- float64  // can only be used to send float64s
-<-chan int      // can only be used to receive ints
-
- -

-The <- operator associates with the leftmost chan -possible: -

- -
-chan<- chan int    // same as chan<- (chan int)
-chan<- <-chan int  // same as chan<- (<-chan int)
-<-chan <-chan int  // same as <-chan (<-chan int)
-chan (<-chan int)
-
- -

-A new, initialized channel -value can be made using the built-in function -make, -which takes the channel type and an optional capacity as arguments: -

- -
-make(chan int, 100)
-
- -

-The capacity, in number of elements, sets the size of the buffer in the channel. -If the capacity is zero or absent, the channel is unbuffered and communication -succeeds only when both a sender and receiver are ready. Otherwise, the channel -is buffered and communication succeeds without blocking if the buffer -is not full (sends) or not empty (receives). -A nil channel is never ready for communication. -

- -

-A channel may be closed with the built-in function -close. -The multi-valued assignment form of the -receive operator -reports whether a received value was sent before -the channel was closed. -

- -

-A single channel may be used in -send statements, -receive operations, -and calls to the built-in functions -cap and -len -by any number of goroutines without further synchronization. -Channels act as first-in-first-out queues. -For example, if one goroutine sends values on a channel -and a second goroutine receives them, the values are -received in the order sent. -

- -

Properties of types and values

- -

Type identity

- -

-Two types are either identical or different. -

- -

-A defined type is always different from any other type. -Otherwise, two types are identical if their underlying type literals are -structurally equivalent; that is, they have the same literal structure and corresponding -components have identical types. In detail: -

- - - -

-Given the declarations -

- -
-type (
-	A0 = []string
-	A1 = A0
-	A2 = struct{ a, b int }
-	A3 = int
-	A4 = func(A3, float64) *A0
-	A5 = func(x int, _ float64) *[]string
-)
-
-type (
-	B0 A0
-	B1 []string
-	B2 struct{ a, b int }
-	B3 struct{ a, c int }
-	B4 func(int, float64) *B0
-	B5 func(x int, y float64) *A1
-)
-
-type	C0 = B0
-
- -

-these types are identical: -

- -
-A0, A1, and []string
-A2 and struct{ a, b int }
-A3 and int
-A4, func(int, float64) *[]string, and A5
-
-B0 and C0
-[]int and []int
-struct{ a, b *T5 } and struct{ a, b *T5 }
-func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5
-
- -

-B0 and B1 are different because they are new types -created by distinct type definitions; -func(int, float64) *B0 and func(x int, y float64) *[]string -are different because B0 is different from []string. -

- - -

Assignability

- -

-A value x is assignable to a variable of type T -("x is assignable to T") if one of the following conditions applies: -

- - - - -

Representability

- -

-A constant x is representable -by a value of type T if one of the following conditions applies: -

- - - -
-x                   T           x is representable by a value of T because
-
-'a'                 byte        97 is in the set of byte values
-97                  rune        rune is an alias for int32, and 97 is in the set of 32-bit integers
-"foo"               string      "foo" is in the set of string values
-1024                int16       1024 is in the set of 16-bit integers
-42.0                byte        42 is in the set of unsigned 8-bit integers
-1e10                uint64      10000000000 is in the set of unsigned 64-bit integers
-2.718281828459045   float32     2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
--1e-1000            float64     -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
-0i                  int         0 is an integer value
-(42 + 0i)           float32     42.0 (with zero imaginary part) is in the set of float32 values
-
- -
-x                   T           x is not representable by a value of T because
-
-0                   bool        0 is not in the set of boolean values
-'a'                 string      'a' is a rune, it is not in the set of string values
-1024                byte        1024 is not in the set of unsigned 8-bit integers
--1                  uint16      -1 is not in the set of unsigned 16-bit integers
-1.1                 int         1.1 is not an integer value
-42i                 float32     (0 + 42i) is not in the set of float32 values
-1e1000              float64     1e1000 overflows to IEEE +Inf after rounding
-
- - -

Blocks

- -

-A block is a possibly empty sequence of declarations and statements -within matching brace brackets. -

- -
-Block = "{" StatementList "}" .
-StatementList = { Statement ";" } .
-
- -

-In addition to explicit blocks in the source code, there are implicit blocks: -

- -
    -
  1. The universe block encompasses all Go source text.
  2. - -
  3. Each package has a package block containing all - Go source text for that package.
  4. - -
  5. Each file has a file block containing all Go source text - in that file.
  6. - -
  7. Each "if", - "for", and - "switch" - statement is considered to be in its own implicit block.
  8. - -
  9. Each clause in a "switch" - or "select" statement - acts as an implicit block.
  10. -
- -

-Blocks nest and influence scoping. -

- - -

Declarations and scope

- -

-A declaration binds a non-blank identifier to a -constant, -type, -variable, -function, -label, or -package. -Every identifier in a program must be declared. -No identifier may be declared twice in the same block, and -no identifier may be declared in both the file and package block. -

- -

-The blank identifier may be used like any other identifier -in a declaration, but it does not introduce a binding and thus is not declared. -In the package block, the identifier init may only be used for -init function declarations, -and like the blank identifier it does not introduce a new binding. -

- -
-Declaration   = ConstDecl | TypeDecl | VarDecl .
-TopLevelDecl  = Declaration | FunctionDecl | MethodDecl .
-
- -

-The scope of a declared identifier is the extent of source text in which -the identifier denotes the specified constant, type, variable, function, label, or package. -

- -

-Go is lexically scoped using blocks: -

- -
    -
  1. The scope of a predeclared identifier is the universe block.
  2. - -
  3. The scope of an identifier denoting a constant, type, variable, - or function (but not method) declared at top level (outside any - function) is the package block.
  4. - -
  5. The scope of the package name of an imported package is the file block - of the file containing the import declaration.
  6. - -
  7. The scope of an identifier denoting a method receiver, function parameter, - or result variable is the function body.
  8. - -
  9. The scope of a constant or variable identifier declared - inside a function begins at the end of the ConstSpec or VarSpec - (ShortVarDecl for short variable declarations) - and ends at the end of the innermost containing block.
  10. - -
  11. The scope of a type identifier declared inside a function - begins at the identifier in the TypeSpec - and ends at the end of the innermost containing block.
  12. -
- -

-An identifier declared in a block may be redeclared in an inner block. -While the identifier of the inner declaration is in scope, it denotes -the entity declared by the inner declaration. -

- -

-The package clause is not a declaration; the package name -does not appear in any scope. Its purpose is to identify the files belonging -to the same package and to specify the default package name for import -declarations. -

- - -

Label scopes

- -

-Labels are declared by labeled statements and are -used in the "break", -"continue", and -"goto" statements. -It is illegal to define a label that is never used. -In contrast to other identifiers, labels are not block scoped and do -not conflict with identifiers that are not labels. The scope of a label -is the body of the function in which it is declared and excludes -the body of any nested function. -

- - -

Blank identifier

- -

-The blank identifier is represented by the underscore character _. -It serves as an anonymous placeholder instead of a regular (non-blank) -identifier and has special meaning in declarations, -as an operand, and in assignments. -

- - -

Predeclared identifiers

- -

-The following identifiers are implicitly declared in the -universe block: -

-
-Types:
-	bool byte complex64 complex128 error float32 float64
-	int int8 int16 int32 int64 rune string
-	uint uint8 uint16 uint32 uint64 uintptr
-
-Constants:
-	true false iota
-
-Zero value:
-	nil
-
-Functions:
-	append cap close complex copy delete imag len
-	make new panic print println real recover
-
- - -

Exported identifiers

- -

-An identifier may be exported to permit access to it from another package. -An identifier is exported if both: -

-
    -
  1. the first character of the identifier's name is a Unicode upper case - letter (Unicode class "Lu"); and
  2. -
  3. the identifier is declared in the package block - or it is a field name or - method name.
  4. -
-

-All other identifiers are not exported. -

- - -

Uniqueness of identifiers

- -

-Given a set of identifiers, an identifier is called unique if it is -different from every other in the set. -Two identifiers are different if they are spelled differently, or if they -appear in different packages and are not -exported. Otherwise, they are the same. -

- -

Constant declarations

- -

-A constant declaration binds a list of identifiers (the names of -the constants) to the values of a list of constant expressions. -The number of identifiers must be equal -to the number of expressions, and the nth identifier on -the left is bound to the value of the nth expression on the -right. -

- -
-ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
-ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .
-
-IdentifierList = identifier { "," identifier } .
-ExpressionList = Expression { "," Expression } .
-
- -

-If the type is present, all constants take the type specified, and -the expressions must be assignable to that type. -If the type is omitted, the constants take the -individual types of the corresponding expressions. -If the expression values are untyped constants, -the declared constants remain untyped and the constant identifiers -denote the constant values. For instance, if the expression is a -floating-point literal, the constant identifier denotes a floating-point -constant, even if the literal's fractional part is zero. -

- -
-const Pi float64 = 3.14159265358979323846
-const zero = 0.0         // untyped floating-point constant
-const (
-	size int64 = 1024
-	eof        = -1  // untyped integer constant
-)
-const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo", untyped integer and string constants
-const u, v float32 = 0, 3    // u = 0.0, v = 3.0
-
- -

-Within a parenthesized const declaration list the -expression list may be omitted from any but the first ConstSpec. -Such an empty list is equivalent to the textual substitution of the -first preceding non-empty expression list and its type if any. -Omitting the list of expressions is therefore equivalent to -repeating the previous list. The number of identifiers must be equal -to the number of expressions in the previous list. -Together with the iota constant generator -this mechanism permits light-weight declaration of sequential values: -

- -
-const (
-	Sunday = iota
-	Monday
-	Tuesday
-	Wednesday
-	Thursday
-	Friday
-	Partyday
-	numberOfDays  // this constant is not exported
-)
-
- - -

Iota

- -

-Within a constant declaration, the predeclared identifier -iota represents successive untyped integer -constants. Its value is the index of the respective ConstSpec -in that constant declaration, starting at zero. -It can be used to construct a set of related constants: -

- -
-const (
-	c0 = iota  // c0 == 0
-	c1 = iota  // c1 == 1
-	c2 = iota  // c2 == 2
-)
-
-const (
-	a = 1 << iota  // a == 1  (iota == 0)
-	b = 1 << iota  // b == 2  (iota == 1)
-	c = 3          // c == 3  (iota == 2, unused)
-	d = 1 << iota  // d == 8  (iota == 3)
-)
-
-const (
-	u         = iota * 42  // u == 0     (untyped integer constant)
-	v float64 = iota * 42  // v == 42.0  (float64 constant)
-	w         = iota * 42  // w == 84    (untyped integer constant)
-)
-
-const x = iota  // x == 0
-const y = iota  // y == 0
-
- -

-By definition, multiple uses of iota in the same ConstSpec all have the same value: -

- -
-const (
-	bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)
-	bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)
-	_, _                                  //                        (iota == 2, unused)
-	bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
-)
-
- -

-This last example exploits the implicit repetition -of the last non-empty expression list. -

- - -

Type declarations

- -

-A type declaration binds an identifier, the type name, to a type. -Type declarations come in two forms: alias declarations and type definitions. -

- -
-TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
-TypeSpec = AliasDecl | TypeDef .
-
- -

Alias declarations

- -

-An alias declaration binds an identifier to the given type. -

- -
-AliasDecl = identifier "=" Type .
-
- -

-Within the scope of -the identifier, it serves as an alias for the type. -

- -
-type (
-	nodeList = []*Node  // nodeList and []*Node are identical types
-	Polar    = polar    // Polar and polar denote identical types
-)
-
- - -

Type definitions

- -

-A type definition creates a new, distinct type with the same -underlying type and operations as the given type, -and binds an identifier to it. -

- -
-TypeDef = identifier Type .
-
- -

-The new type is called a defined type. -It is different from any other type, -including the type it is created from. -

- -
-type (
-	Point struct{ x, y float64 }  // Point and struct{ x, y float64 } are different types
-	polar Point                   // polar and Point denote different types
-)
-
-type TreeNode struct {
-	left, right *TreeNode
-	value *Comparable
-}
-
-type Block interface {
-	BlockSize() int
-	Encrypt(src, dst []byte)
-	Decrypt(src, dst []byte)
-}
-
- -

-A defined type may have methods associated with it. -It does not inherit any methods bound to the given type, -but the method set -of an interface type or of elements of a composite type remains unchanged: -

- -
-// A Mutex is a data type with two methods, Lock and Unlock.
-type Mutex struct         { /* Mutex fields */ }
-func (m *Mutex) Lock()    { /* Lock implementation */ }
-func (m *Mutex) Unlock()  { /* Unlock implementation */ }
-
-// NewMutex has the same composition as Mutex but its method set is empty.
-type NewMutex Mutex
-
-// The method set of PtrMutex's underlying type *Mutex remains unchanged,
-// but the method set of PtrMutex is empty.
-type PtrMutex *Mutex
-
-// The method set of *PrintableMutex contains the methods
-// Lock and Unlock bound to its embedded field Mutex.
-type PrintableMutex struct {
-	Mutex
-}
-
-// MyBlock is an interface type that has the same method set as Block.
-type MyBlock Block
-
- -

-Type definitions may be used to define different boolean, numeric, -or string types and associate methods with them: -

- -
-type TimeZone int
-
-const (
-	EST TimeZone = -(5 + iota)
-	CST
-	MST
-	PST
-)
-
-func (tz TimeZone) String() string {
-	return fmt.Sprintf("GMT%+dh", tz)
-}
-
- - -

Variable declarations

- -

-A variable declaration creates one or more variables, -binds corresponding identifiers to them, and gives each a type and an initial value. -

- -
-VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
-VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
-
- -
-var i int
-var U, V, W float64
-var k = 0
-var x, y float32 = -1, -2
-var (
-	i       int
-	u, v, s = 2.0, 3.0, "bar"
-)
-var re, im = complexSqrt(-1)
-var _, found = entries[name]  // map lookup; only interested in "found"
-
- -

-If a list of expressions is given, the variables are initialized -with the expressions following the rules for assignments. -Otherwise, each variable is initialized to its zero value. -

- -

-If a type is present, each variable is given that type. -Otherwise, each variable is given the type of the corresponding -initialization value in the assignment. -If that value is an untyped constant, it is first implicitly -converted to its default type; -if it is an untyped boolean value, it is first implicitly converted to type bool. -The predeclared value nil cannot be used to initialize a variable -with no explicit type. -

- -
-var d = math.Sin(0.5)  // d is float64
-var i = 42             // i is int
-var t, ok = x.(T)      // t is T, ok is bool
-var n = nil            // illegal
-
- -

-Implementation restriction: A compiler may make it illegal to declare a variable -inside a function body if the variable is -never used. -

- -

Short variable declarations

- -

-A short variable declaration uses the syntax: -

- -
-ShortVarDecl = IdentifierList ":=" ExpressionList .
-
- -

-It is shorthand for a regular variable declaration -with initializer expressions but no types: -

- -
-"var" IdentifierList = ExpressionList .
-
- -
-i, j := 0, 10
-f := func() int { return 7 }
-ch := make(chan int)
-r, w, _ := os.Pipe()  // os.Pipe() returns a connected pair of Files and an error, if any
-_, y, _ := coord(p)   // coord() returns three values; only interested in y coordinate
-
- -

-Unlike regular variable declarations, a short variable declaration may redeclare -variables provided they were originally declared earlier in the same block -(or the parameter lists if the block is the function body) with the same type, -and at least one of the non-blank variables is new. -As a consequence, redeclaration can only appear in a multi-variable short declaration. -Redeclaration does not introduce a new variable; it just assigns a new value to the original. -

- -
-field1, offset := nextField(str, 0)
-field2, offset := nextField(str, offset)  // redeclares offset
-a, a := 1, 2                              // illegal: double declaration of a or no new variable if a was declared elsewhere
-
- -

-Short variable declarations may appear only inside functions. -In some contexts such as the initializers for -"if", -"for", or -"switch" statements, -they can be used to declare local temporary variables. -

- -

Function declarations

- -

-A function declaration binds an identifier, the function name, -to a function. -

- -
-FunctionDecl = "func" FunctionName Signature [ FunctionBody ] .
-FunctionName = identifier .
-FunctionBody = Block .
-
- -

-If the function's signature declares -result parameters, the function body's statement list must end in -a terminating statement. -

- -
-func IndexRune(s string, r rune) int {
-	for i, c := range s {
-		if c == r {
-			return i
-		}
-	}
-	// invalid: missing return statement
-}
-
- -

-A function declaration may omit the body. Such a declaration provides the -signature for a function implemented outside Go, such as an assembly routine. -

- -
-func min(x int, y int) int {
-	if x < y {
-		return x
-	}
-	return y
-}
-
-func flushICache(begin, end uintptr)  // implemented externally
-
- -

Method declarations

- -

-A method is a function with a receiver. -A method declaration binds an identifier, the method name, to a method, -and associates the method with the receiver's base type. -

- -
-MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
-Receiver   = Parameters .
-
- -

-The receiver is specified via an extra parameter section preceding the method -name. That parameter section must declare a single non-variadic parameter, the receiver. -Its type must be a defined type T or a -pointer to a defined type T. T is called the receiver -base type. A receiver base type cannot be a pointer or interface type and -it must be defined in the same package as the method. -The method is said to be bound to its receiver base type and the method name -is visible only within selectors for type T -or *T. -

- -

-A non-blank receiver identifier must be -unique in the method signature. -If the receiver's value is not referenced inside the body of the method, -its identifier may be omitted in the declaration. The same applies in -general to parameters of functions and methods. -

- -

-For a base type, the non-blank names of methods bound to it must be unique. -If the base type is a struct type, -the non-blank method and field names must be distinct. -

- -

-Given defined type Point, the declarations -

- -
-func (p *Point) Length() float64 {
-	return math.Sqrt(p.x * p.x + p.y * p.y)
-}
-
-func (p *Point) Scale(factor float64) {
-	p.x *= factor
-	p.y *= factor
-}
-
- -

-bind the methods Length and Scale, -with receiver type *Point, -to the base type Point. -

- -

-The type of a method is the type of a function with the receiver as first -argument. For instance, the method Scale has type -

- -
-func(p *Point, factor float64)
-
- -

-However, a function declared this way is not a method. -

- - -

Expressions

- -

-An expression specifies the computation of a value by applying -operators and functions to operands. -

- -

Operands

- -

-Operands denote the elementary values in an expression. An operand may be a -literal, a (possibly qualified) -non-blank identifier denoting a -constant, -variable, or -function, -or a parenthesized expression. -

- -

-The blank identifier may appear as an -operand only on the left-hand side of an assignment. -

- -
-Operand     = Literal | OperandName | "(" Expression ")" .
-Literal     = BasicLit | CompositeLit | FunctionLit .
-BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
-OperandName = identifier | QualifiedIdent .
-
- -

Qualified identifiers

- -

-A qualified identifier is an identifier qualified with a package name prefix. -Both the package name and the identifier must not be -blank. -

- -
-QualifiedIdent = PackageName "." identifier .
-
- -

-A qualified identifier accesses an identifier in a different package, which -must be imported. -The identifier must be exported and -declared in the package block of that package. -

- -
-math.Sin	// denotes the Sin function in package math
-
- -

Composite literals

- -

-Composite literals construct values for structs, arrays, slices, and maps -and create a new value each time they are evaluated. -They consist of the type of the literal followed by a brace-bound list of elements. -Each element may optionally be preceded by a corresponding key. -

- -
-CompositeLit  = LiteralType LiteralValue .
-LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
-                SliceType | MapType | TypeName .
-LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
-ElementList   = KeyedElement { "," KeyedElement } .
-KeyedElement  = [ Key ":" ] Element .
-Key           = FieldName | Expression | LiteralValue .
-FieldName     = identifier .
-Element       = Expression | LiteralValue .
-
- -

-The LiteralType's underlying type must be a struct, array, slice, or map type -(the grammar enforces this constraint except when the type is given -as a TypeName). -The types of the elements and keys must be assignable -to the respective field, element, and key types of the literal type; -there is no additional conversion. -The key is interpreted as a field name for struct literals, -an index for array and slice literals, and a key for map literals. -For map literals, all elements must have a key. It is an error -to specify multiple elements with the same field name or -constant key value. For non-constant map keys, see the section on -evaluation order. -

- -

-For struct literals the following rules apply: -

- - -

-Given the declarations -

-
-type Point3D struct { x, y, z float64 }
-type Line struct { p, q Point3D }
-
- -

-one may write -

- -
-origin := Point3D{}                            // zero value for Point3D
-line := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x
-
- -

-For array and slice literals the following rules apply: -

- - -

-Taking the address of a composite literal -generates a pointer to a unique variable initialized -with the literal's value. -

- -
-var pointer *Point3D = &Point3D{y: 1000}
-
- -

-Note that the zero value for a slice or map -type is not the same as an initialized but empty value of the same type. -Consequently, taking the address of an empty slice or map composite literal -does not have the same effect as allocating a new slice or map value with -new. -

- -
-p1 := &[]int{}    // p1 points to an initialized, empty slice with value []int{} and length 0
-p2 := new([]int)  // p2 points to an uninitialized slice with value nil and length 0
-
- -

-The length of an array literal is the length specified in the literal type. -If fewer elements than the length are provided in the literal, the missing -elements are set to the zero value for the array element type. -It is an error to provide elements with index values outside the index range -of the array. The notation ... specifies an array length equal -to the maximum element index plus one. -

- -
-buffer := [10]string{}             // len(buffer) == 10
-intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
-days := [...]string{"Sat", "Sun"}  // len(days) == 2
-
- -

-A slice literal describes the entire underlying array literal. -Thus the length and capacity of a slice literal are the maximum -element index plus one. A slice literal has the form -

- -
-[]T{x1, x2, … xn}
-
- -

-and is shorthand for a slice operation applied to an array: -

- -
-tmp := [n]T{x1, x2, … xn}
-tmp[0 : n]
-
- -

-Within a composite literal of array, slice, or map type T, -elements or map keys that are themselves composite literals may elide the respective -literal type if it is identical to the element or key type of T. -Similarly, elements or keys that are addresses of composite literals may elide -the &T when the element or key type is *T. -

- -
-[...]Point{{1.5, -3.5}, {0, 0}}     // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
-[][]int{{1, 2, 3}, {4, 5}}          // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
-[][]Point{{{0, 1}, {1, 2}}}         // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
-map[string]Point{"orig": {0, 0}}    // same as map[string]Point{"orig": Point{0, 0}}
-map[Point]string{{0, 0}: "orig"}    // same as map[Point]string{Point{0, 0}: "orig"}
-
-type PPoint *Point
-[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
-[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}
-
- -

-A parsing ambiguity arises when a composite literal using the -TypeName form of the LiteralType appears as an operand between the -keyword and the opening brace of the block -of an "if", "for", or "switch" statement, and the composite literal -is not enclosed in parentheses, square brackets, or curly braces. -In this rare case, the opening brace of the literal is erroneously parsed -as the one introducing the block of statements. To resolve the ambiguity, -the composite literal must appear within parentheses. -

- -
-if x == (T{a,b,c}[i]) { … }
-if (x == T{a,b,c}[i]) { … }
-
- -

-Examples of valid array, slice, and map literals: -

- -
-// list of prime numbers
-primes := []int{2, 3, 5, 7, 9, 2147483647}
-
-// vowels[ch] is true if ch is a vowel
-vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}
-
-// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
-filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}
-
-// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
-noteFrequency := map[string]float32{
-	"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,
-	"G0": 24.50, "A0": 27.50, "B0": 30.87,
-}
-
- - -

Function literals

- -

-A function literal represents an anonymous function. -

- -
-FunctionLit = "func" Signature FunctionBody .
-
- -
-func(a, b int, z float64) bool { return a*b < int(z) }
-
- -

-A function literal can be assigned to a variable or invoked directly. -

- -
-f := func(x, y int) int { return x + y }
-func(ch chan int) { ch <- ACK }(replyChan)
-
- -

-Function literals are closures: they may refer to variables -defined in a surrounding function. Those variables are then shared between -the surrounding function and the function literal, and they survive as long -as they are accessible. -

- - -

Primary expressions

- -

-Primary expressions are the operands for unary and binary expressions. -

- -
-PrimaryExpr =
-	Operand |
-	Conversion |
-	MethodExpr |
-	PrimaryExpr Selector |
-	PrimaryExpr Index |
-	PrimaryExpr Slice |
-	PrimaryExpr TypeAssertion |
-	PrimaryExpr Arguments .
-
-Selector       = "." identifier .
-Index          = "[" Expression "]" .
-Slice          = "[" [ Expression ] ":" [ Expression ] "]" |
-                 "[" [ Expression ] ":" Expression ":" Expression "]" .
-TypeAssertion  = "." "(" Type ")" .
-Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
-
- - -
-x
-2
-(s + ".txt")
-f(3.1415, true)
-Point{1, 2}
-m["foo"]
-s[i : j + 1]
-obj.color
-f.p[i].x()
-
- - -

Selectors

- -

-For a primary expression x -that is not a package name, the -selector expression -

- -
-x.f
-
- -

-denotes the field or method f of the value x -(or sometimes *x; see below). -The identifier f is called the (field or method) selector; -it must not be the blank identifier. -The type of the selector expression is the type of f. -If x is a package name, see the section on -qualified identifiers. -

- -

-A selector f may denote a field or method f of -a type T, or it may refer -to a field or method f of a nested -embedded field of T. -The number of embedded fields traversed -to reach f is called its depth in T. -The depth of a field or method f -declared in T is zero. -The depth of a field or method f declared in -an embedded field A in T is the -depth of f in A plus one. -

- -

-The following rules apply to selectors: -

- -
    -
  1. -For a value x of type T or *T -where T is not a pointer or interface type, -x.f denotes the field or method at the shallowest depth -in T where there -is such an f. -If there is not exactly one f -with shallowest depth, the selector expression is illegal. -
  2. - -
  3. -For a value x of type I where I -is an interface type, x.f denotes the actual method with name -f of the dynamic value of x. -If there is no method with name f in the -method set of I, the selector -expression is illegal. -
  4. - -
  5. -As an exception, if the type of x is a defined -pointer type and (*x).f is a valid selector expression denoting a field -(but not a method), x.f is shorthand for (*x).f. -
  6. - -
  7. -In all other cases, x.f is illegal. -
  8. - -
  9. -If x is of pointer type and has the value -nil and x.f denotes a struct field, -assigning to or evaluating x.f -causes a run-time panic. -
  10. - -
  11. -If x is of interface type and has the value -nil, calling or -evaluating the method x.f -causes a run-time panic. -
  12. -
- -

-For example, given the declarations: -

- -
-type T0 struct {
-	x int
-}
-
-func (*T0) M0()
-
-type T1 struct {
-	y int
-}
-
-func (T1) M1()
-
-type T2 struct {
-	z int
-	T1
-	*T0
-}
-
-func (*T2) M2()
-
-type Q *T2
-
-var t T2     // with t.T0 != nil
-var p *T2    // with p != nil and (*p).T0 != nil
-var q Q = p
-
- -

-one may write: -

- -
-t.z          // t.z
-t.y          // t.T1.y
-t.x          // (*t.T0).x
-
-p.z          // (*p).z
-p.y          // (*p).T1.y
-p.x          // (*(*p).T0).x
-
-q.x          // (*(*q).T0).x        (*q).x is a valid field selector
-
-p.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver
-p.M1()       // ((*p).T1).M1()      M1 expects T1 receiver
-p.M2()       // p.M2()              M2 expects *T2 receiver
-t.M2()       // (&t).M2()           M2 expects *T2 receiver, see section on Calls
-
- -

-but the following is invalid: -

- -
-q.M0()       // (*q).M0 is valid but not a field selector
-
- - -

Method expressions

- -

-If M is in the method set of type T, -T.M is a function that is callable as a regular function -with the same arguments as M prefixed by an additional -argument that is the receiver of the method. -

- -
-MethodExpr    = ReceiverType "." MethodName .
-ReceiverType  = Type .
-
- -

-Consider a struct type T with two methods, -Mv, whose receiver is of type T, and -Mp, whose receiver is of type *T. -

- -
-type T struct {
-	a int
-}
-func (tv  T) Mv(a int) int         { return 0 }  // value receiver
-func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver
-
-var t T
-
- -

-The expression -

- -
-T.Mv
-
- -

-yields a function equivalent to Mv but -with an explicit receiver as its first argument; it has signature -

- -
-func(tv T, a int) int
-
- -

-That function may be called normally with an explicit receiver, so -these five invocations are equivalent: -

- -
-t.Mv(7)
-T.Mv(t, 7)
-(T).Mv(t, 7)
-f1 := T.Mv; f1(t, 7)
-f2 := (T).Mv; f2(t, 7)
-
- -

-Similarly, the expression -

- -
-(*T).Mp
-
- -

-yields a function value representing Mp with signature -

- -
-func(tp *T, f float32) float32
-
- -

-For a method with a value receiver, one can derive a function -with an explicit pointer receiver, so -

- -
-(*T).Mv
-
- -

-yields a function value representing Mv with signature -

- -
-func(tv *T, a int) int
-
- -

-Such a function indirects through the receiver to create a value -to pass as the receiver to the underlying method; -the method does not overwrite the value whose address is passed in -the function call. -

- -

-The final case, a value-receiver function for a pointer-receiver method, -is illegal because pointer-receiver methods are not in the method set -of the value type. -

- -

-Function values derived from methods are called with function call syntax; -the receiver is provided as the first argument to the call. -That is, given f := T.Mv, f is invoked -as f(t, 7) not t.f(7). -To construct a function that binds the receiver, use a -function literal or -method value. -

- -

-It is legal to derive a function value from a method of an interface type. -The resulting function takes an explicit receiver of that interface type. -

- -

Method values

- -

-If the expression x has static type T and -M is in the method set of type T, -x.M is called a method value. -The method value x.M is a function value that is callable -with the same arguments as a method call of x.M. -The expression x is evaluated and saved during the evaluation of the -method value; the saved copy is then used as the receiver in any calls, -which may be executed later. -

- -
-type S struct { *T }
-type T int
-func (t T) M() { print(t) }
-
-t := new(T)
-s := S{T: t}
-f := t.M                    // receiver *t is evaluated and stored in f
-g := s.M                    // receiver *(s.T) is evaluated and stored in g
-*t = 42                     // does not affect stored receivers in f and g
-
- -

-The type T may be an interface or non-interface type. -

- -

-As in the discussion of method expressions above, -consider a struct type T with two methods, -Mv, whose receiver is of type T, and -Mp, whose receiver is of type *T. -

- -
-type T struct {
-	a int
-}
-func (tv  T) Mv(a int) int         { return 0 }  // value receiver
-func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receiver
-
-var t T
-var pt *T
-func makeT() T
-
- -

-The expression -

- -
-t.Mv
-
- -

-yields a function value of type -

- -
-func(int) int
-
- -

-These two invocations are equivalent: -

- -
-t.Mv(7)
-f := t.Mv; f(7)
-
- -

-Similarly, the expression -

- -
-pt.Mp
-
- -

-yields a function value of type -

- -
-func(float32) float32
-
- -

-As with selectors, a reference to a non-interface method with a value receiver -using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv. -

- -

-As with method calls, a reference to a non-interface method with a pointer receiver -using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp. -

- -
-f := t.Mv; f(7)   // like t.Mv(7)
-f := pt.Mp; f(7)  // like pt.Mp(7)
-f := pt.Mv; f(7)  // like (*pt).Mv(7)
-f := t.Mp; f(7)   // like (&t).Mp(7)
-f := makeT().Mp   // invalid: result of makeT() is not addressable
-
- -

-Although the examples above use non-interface types, it is also legal to create a method value -from a value of interface type. -

- -
-var i interface { M(int) } = myVal
-f := i.M; f(7)  // like i.M(7)
-
- - -

Index expressions

- -

-A primary expression of the form -

- -
-a[x]
-
- -

-denotes the element of the array, pointer to array, slice, string or map a indexed by x. -The value x is called the index or map key, respectively. -The following rules apply: -

- -

-If a is not a map: -

- - -

-For a of array type A: -

- - -

-For a of pointer to array type: -

- - -

-For a of slice type S: -

- - -

-For a of string type: -

- - -

-For a of map type M: -

- - -

-Otherwise a[x] is illegal. -

- -

-An index expression on a map a of type map[K]V -used in an assignment or initialization of the special form -

- -
-v, ok = a[x]
-v, ok := a[x]
-var v, ok = a[x]
-
- -

-yields an additional untyped boolean value. The value of ok is -true if the key x is present in the map, and -false otherwise. -

- -

-Assigning to an element of a nil map causes a -run-time panic. -

- - -

Slice expressions

- -

-Slice expressions construct a substring or slice from a string, array, pointer -to array, or slice. There are two variants: a simple form that specifies a low -and high bound, and a full form that also specifies a bound on the capacity. -

- -

Simple slice expressions

- -

-For a string, array, pointer to array, or slice a, the primary expression -

- -
-a[low : high]
-
- -

-constructs a substring or slice. The indices low and -high select which elements of operand a appear -in the result. The result has indices starting at 0 and length equal to -high - low. -After slicing the array a -

- -
-a := [5]int{1, 2, 3, 4, 5}
-s := a[1:4]
-
- -

-the slice s has type []int, length 3, capacity 4, and elements -

- -
-s[0] == 2
-s[1] == 3
-s[2] == 4
-
- -

-For convenience, any of the indices may be omitted. A missing low -index defaults to zero; a missing high index defaults to the length of the -sliced operand: -

- -
-a[2:]  // same as a[2 : len(a)]
-a[:3]  // same as a[0 : 3]
-a[:]   // same as a[0 : len(a)]
-
- -

-If a is a pointer to an array, a[low : high] is shorthand for -(*a)[low : high]. -

- -

-For arrays or strings, the indices are in range if -0 <= low <= high <= len(a), -otherwise they are out of range. -For slices, the upper index bound is the slice capacity cap(a) rather than the length. -A constant index must be non-negative and -representable by a value of type -int; for arrays or constant strings, constant indices must also be in range. -If both indices are constant, they must satisfy low <= high. -If the indices are out of range at run time, a run-time panic occurs. -

- -

-Except for untyped strings, if the sliced operand is a string or slice, -the result of the slice operation is a non-constant value of the same type as the operand. -For untyped string operands the result is a non-constant value of type string. -If the sliced operand is an array, it must be addressable -and the result of the slice operation is a slice with the same element type as the array. -

- -

-If the sliced operand of a valid slice expression is a nil slice, the result -is a nil slice. Otherwise, if the result is a slice, it shares its underlying -array with the operand. -

- -
-var a [10]int
-s1 := a[3:7]   // underlying array of s1 is array a; &s1[2] == &a[5]
-s2 := s1[1:4]  // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &a[5]
-s2[1] = 42     // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array element
-
- - -

Full slice expressions

- -

-For an array, pointer to array, or slice a (but not a string), the primary expression -

- -
-a[low : high : max]
-
- -

-constructs a slice of the same type, and with the same length and elements as the simple slice -expression a[low : high]. Additionally, it controls the resulting slice's capacity -by setting it to max - low. Only the first index may be omitted; it defaults to 0. -After slicing the array a -

- -
-a := [5]int{1, 2, 3, 4, 5}
-t := a[1:3:5]
-
- -

-the slice t has type []int, length 2, capacity 4, and elements -

- -
-t[0] == 2
-t[1] == 3
-
- -

-As for simple slice expressions, if a is a pointer to an array, -a[low : high : max] is shorthand for (*a)[low : high : max]. -If the sliced operand is an array, it must be addressable. -

- -

-The indices are in range if 0 <= low <= high <= max <= cap(a), -otherwise they are out of range. -A constant index must be non-negative and -representable by a value of type -int; for arrays, constant indices must also be in range. -If multiple indices are constant, the constants that are present must be in range relative to each -other. -If the indices are out of range at run time, a run-time panic occurs. -

- -

Type assertions

- -

-For an expression x of interface type -and a type T, the primary expression -

- -
-x.(T)
-
- -

-asserts that x is not nil -and that the value stored in x is of type T. -The notation x.(T) is called a type assertion. -

-

-More precisely, if T is not an interface type, x.(T) asserts -that the dynamic type of x is identical -to the type T. -In this case, T must implement the (interface) type of x; -otherwise the type assertion is invalid since it is not possible for x -to store a value of type T. -If T is an interface type, x.(T) asserts that the dynamic type -of x implements the interface T. -

-

-If the type assertion holds, the value of the expression is the value -stored in x and its type is T. If the type assertion is false, -a run-time panic occurs. -In other words, even though the dynamic type of x -is known only at run time, the type of x.(T) is -known to be T in a correct program. -

- -
-var x interface{} = 7          // x has dynamic type int and value 7
-i := x.(int)                   // i has type int and value 7
-
-type I interface { m() }
-
-func f(y I) {
-	s := y.(string)        // illegal: string does not implement I (missing method m)
-	r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader
-	…
-}
-
- -

-A type assertion used in an assignment or initialization of the special form -

- -
-v, ok = x.(T)
-v, ok := x.(T)
-var v, ok = x.(T)
-var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool
-
- -

-yields an additional untyped boolean value. The value of ok is true -if the assertion holds. Otherwise it is false and the value of v is -the zero value for type T. -No run-time panic occurs in this case. -

- - -

Calls

- -

-Given an expression f of function type -F, -

- -
-f(a1, a2, … an)
-
- -

-calls f with arguments a1, a2, … an. -Except for one special case, arguments must be single-valued expressions -assignable to the parameter types of -F and are evaluated before the function is called. -The type of the expression is the result type -of F. -A method invocation is similar but the method itself -is specified as a selector upon a value of the receiver type for -the method. -

- -
-math.Atan2(x, y)  // function call
-var pt *Point
-pt.Scale(3.5)     // method call with receiver pt
-
- -

-In a function call, the function value and arguments are evaluated in -the usual order. -After they are evaluated, the parameters of the call are passed by value to the function -and the called function begins execution. -The return parameters of the function are passed by value -back to the caller when the function returns. -

- -

-Calling a nil function value -causes a run-time panic. -

- -

-As a special case, if the return values of a function or method -g are equal in number and individually -assignable to the parameters of another function or method -f, then the call f(g(parameters_of_g)) -will invoke f after binding the return values of -g to the parameters of f in order. The call -of f must contain no parameters other than the call of g, -and g must have at least one return value. -If f has a final ... parameter, it is -assigned the return values of g that remain after -assignment of regular parameters. -

- -
-func Split(s string, pos int) (string, string) {
-	return s[0:pos], s[pos:]
-}
-
-func Join(s, t string) string {
-	return s + t
-}
-
-if Join(Split(value, len(value)/2)) != value {
-	log.Panic("test fails")
-}
-
- -

-A method call x.m() is valid if the method set -of (the type of) x contains m and the -argument list can be assigned to the parameter list of m. -If x is addressable and &x's method -set contains m, x.m() is shorthand -for (&x).m(): -

- -
-var p Point
-p.Scale(3.5)
-
- -

-There is no distinct method type and there are no method literals. -

- -

Passing arguments to ... parameters

- -

-If f is variadic with a final -parameter p of type ...T, then within f -the type of p is equivalent to type []T. -If f is invoked with no actual arguments for p, -the value passed to p is nil. -Otherwise, the value passed is a new slice -of type []T with a new underlying array whose successive elements -are the actual arguments, which all must be assignable -to T. The length and capacity of the slice is therefore -the number of arguments bound to p and may differ for each -call site. -

- -

-Given the function and calls -

-
-func Greeting(prefix string, who ...string)
-Greeting("nobody")
-Greeting("hello:", "Joe", "Anna", "Eileen")
-
- -

-within Greeting, who will have the value -nil in the first call, and -[]string{"Joe", "Anna", "Eileen"} in the second. -

- -

-If the final argument is assignable to a slice type []T and -is followed by ..., it is passed unchanged as the value -for a ...T parameter. In this case no new slice is created. -

- -

-Given the slice s and call -

- -
-s := []string{"James", "Jasmine"}
-Greeting("goodbye:", s...)
-
- -

-within Greeting, who will have the same value as s -with the same underlying array. -

- - -

Operators

- -

-Operators combine operands into expressions. -

- -
-Expression = UnaryExpr | Expression binary_op Expression .
-UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .
-
-binary_op  = "||" | "&&" | rel_op | add_op | mul_op .
-rel_op     = "==" | "!=" | "<" | "<=" | ">" | ">=" .
-add_op     = "+" | "-" | "|" | "^" .
-mul_op     = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
-
-unary_op   = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
-
- -

-Comparisons are discussed elsewhere. -For other binary operators, the operand types must be identical -unless the operation involves shifts or untyped constants. -For operations involving constants only, see the section on -constant expressions. -

- -

-Except for shift operations, if one operand is an untyped constant -and the other operand is not, the constant is implicitly converted -to the type of the other operand. -

- -

-The right operand in a shift expression must have integer type -or be an untyped constant representable by a -value of type uint. -If the left operand of a non-constant shift expression is an untyped constant, -it is first implicitly converted to the type it would assume if the shift expression were -replaced by its left operand alone. -

- -
-var a [1024]byte
-var s uint = 33
-
-// The results of the following examples are given for 64-bit ints.
-var i = 1<<s                   // 1 has type int
-var j int32 = 1<<s             // 1 has type int32; j == 0
-var k = uint64(1<<s)           // 1 has type uint64; k == 1<<33
-var m int = 1.0<<s             // 1.0 has type int; m == 1<<33
-var n = 1.0<<s == j            // 1.0 has type int32; n == true
-var o = 1<<s == 2<<s           // 1 and 2 have type int; o == false
-var p = 1<<s == 1<<33          // 1 has type int; p == true
-var u = 1.0<<s                 // illegal: 1.0 has type float64, cannot shift
-var u1 = 1.0<<s != 0           // illegal: 1.0 has type float64, cannot shift
-var u2 = 1<<s != 1.0           // illegal: 1 has type float64, cannot shift
-var v float32 = 1<<s           // illegal: 1 has type float32, cannot shift
-var w int64 = 1.0<<33          // 1.0<<33 is a constant shift expression; w == 1<<33
-var x = a[1.0<<s]              // panics: 1.0 has type int, but 1<<33 overflows array bounds
-var b = make([]byte, 1.0<<s)   // 1.0 has type int; len(b) == 1<<33
-
-// The results of the following examples are given for 32-bit ints,
-// which means the shifts will overflow.
-var mm int = 1.0<<s            // 1.0 has type int; mm == 0
-var oo = 1<<s == 2<<s          // 1 and 2 have type int; oo == true
-var pp = 1<<s == 1<<33         // illegal: 1 has type int, but 1<<33 overflows int
-var xx = a[1.0<<s]             // 1.0 has type int; xx == a[0]
-var bb = make([]byte, 1.0<<s)  // 1.0 has type int; len(bb) == 0
-
- -

Operator precedence

-

-Unary operators have the highest precedence. -As the ++ and -- operators form -statements, not expressions, they fall -outside the operator hierarchy. -As a consequence, statement *p++ is the same as (*p)++. -

- -

-There are five precedence levels for binary operators. -Multiplication operators bind strongest, followed by addition -operators, comparison operators, && (logical AND), -and finally || (logical OR): -

- -
-Precedence    Operator
-    5             *  /  %  <<  >>  &  &^
-    4             +  -  |  ^
-    3             ==  !=  <  <=  >  >=
-    2             &&
-    1             ||
-
- -

-Binary operators of the same precedence associate from left to right. -For instance, x / y * z is the same as (x / y) * z. -

- -
-+x
-23 + 3*x[i]
-x <= f()
-^a >> b
-f() || g()
-x == y+1 && <-chanInt > 0
-
- - -

Arithmetic operators

-

-Arithmetic operators apply to numeric values and yield a result of the same -type as the first operand. The four standard arithmetic operators (+, --, *, /) apply to integer, -floating-point, and complex types; + also applies to strings. -The bitwise logical and shift operators apply to integers only. -

- -
-+    sum                    integers, floats, complex values, strings
--    difference             integers, floats, complex values
-*    product                integers, floats, complex values
-/    quotient               integers, floats, complex values
-%    remainder              integers
-
-&    bitwise AND            integers
-|    bitwise OR             integers
-^    bitwise XOR            integers
-&^   bit clear (AND NOT)    integers
-
-<<   left shift             integer << integer >= 0
->>   right shift            integer >> integer >= 0
-
- - -

Integer operators

- -

-For two integer values x and y, the integer quotient -q = x / y and remainder r = x % y satisfy the following -relationships: -

- -
-x = q*y + r  and  |r| < |y|
-
- -

-with x / y truncated towards zero -("truncated division"). -

- -
- x     y     x / y     x % y
- 5     3       1         2
--5     3      -1        -2
- 5    -3      -1         2
--5    -3       1        -2
-
- -

-The one exception to this rule is that if the dividend x is -the most negative value for the int type of x, the quotient -q = x / -1 is equal to x (and r = 0) -due to two's-complement integer overflow: -

- -
-			 x, q
-int8                     -128
-int16                  -32768
-int32             -2147483648
-int64    -9223372036854775808
-
- -

-If the divisor is a constant, it must not be zero. -If the divisor is zero at run time, a run-time panic occurs. -If the dividend is non-negative and the divisor is a constant power of 2, -the division may be replaced by a right shift, and computing the remainder may -be replaced by a bitwise AND operation: -

- -
- x     x / 4     x % 4     x >> 2     x & 3
- 11      2         3         2          3
--11     -2        -3        -3          1
-
- -

-The shift operators shift the left operand by the shift count specified by the -right operand, which must be non-negative. If the shift count is negative at run time, -a run-time panic occurs. -The shift operators implement arithmetic shifts if the left operand is a signed -integer and logical shifts if it is an unsigned integer. -There is no upper limit on the shift count. Shifts behave -as if the left operand is shifted n times by 1 for a shift -count of n. -As a result, x << 1 is the same as x*2 -and x >> 1 is the same as -x/2 but truncated towards negative infinity. -

- -

-For integer operands, the unary operators -+, -, and ^ are defined as -follows: -

- -
-+x                          is 0 + x
--x    negation              is 0 - x
-^x    bitwise complement    is m ^ x  with m = "all bits set to 1" for unsigned x
-                                      and  m = -1 for signed x
-
- - -

Integer overflow

- -

-For unsigned integer values, the operations +, --, *, and << are -computed modulo 2n, where n is the bit width of -the unsigned integer's type. -Loosely speaking, these unsigned integer operations -discard high bits upon overflow, and programs may rely on "wrap around". -

-

-For signed integers, the operations +, --, *, /, and << may legally -overflow and the resulting value exists and is deterministically defined -by the signed integer representation, the operation, and its operands. -Overflow does not cause a run-time panic. -A compiler may not optimize code under the assumption that overflow does -not occur. For instance, it may not assume that x < x + 1 is always true. -

- - -

Floating-point operators

- -

-For floating-point and complex numbers, -+x is the same as x, -while -x is the negation of x. -The result of a floating-point or complex division by zero is not specified beyond the -IEEE 754 standard; whether a run-time panic -occurs is implementation-specific. -

- -

-An implementation may combine multiple floating-point operations into a single -fused operation, possibly across statements, and produce a result that differs -from the value obtained by executing and rounding the instructions individually. -An explicit floating-point type conversion rounds to -the precision of the target type, preventing fusion that would discard that rounding. -

- -

-For instance, some architectures provide a "fused multiply and add" (FMA) instruction -that computes x*y + z without rounding the intermediate result x*y. -These examples show when a Go implementation can use that instruction: -

- -
-// FMA allowed for computing r, because x*y is not explicitly rounded:
-r  = x*y + z
-r  = z;   r += x*y
-t  = x*y; r = t + z
-*p = x*y; r = *p + z
-r  = x*y + float64(z)
-
-// FMA disallowed for computing r, because it would omit rounding of x*y:
-r  = float64(x*y) + z
-r  = z; r += float64(x*y)
-t  = float64(x*y); r = t + z
-
- -

String concatenation

- -

-Strings can be concatenated using the + operator -or the += assignment operator: -

- -
-s := "hi" + string(c)
-s += " and good bye"
-
- -

-String addition creates a new string by concatenating the operands. -

- - -

Comparison operators

- -

-Comparison operators compare two operands and yield an untyped boolean value. -

- -
-==    equal
-!=    not equal
-<     less
-<=    less or equal
->     greater
->=    greater or equal
-
- -

-In any comparison, the first operand -must be assignable -to the type of the second operand, or vice versa. -

-

-The equality operators == and != apply -to operands that are comparable. -The ordering operators <, <=, >, and >= -apply to operands that are ordered. -These terms and the result of the comparisons are defined as follows: -

- - - -

-A comparison of two interface values with identical dynamic types -causes a run-time panic if values -of that type are not comparable. This behavior applies not only to direct interface -value comparisons but also when comparing arrays of interface values -or structs with interface-valued fields. -

- -

-Slice, map, and function values are not comparable. -However, as a special case, a slice, map, or function value may -be compared to the predeclared identifier nil. -Comparison of pointer, channel, and interface values to nil -is also allowed and follows from the general rules above. -

- -
-const c = 3 < 4            // c is the untyped boolean constant true
-
-type MyBool bool
-var x, y int
-var (
-	// The result of a comparison is an untyped boolean.
-	// The usual assignment rules apply.
-	b3        = x == y // b3 has type bool
-	b4 bool   = x == y // b4 has type bool
-	b5 MyBool = x == y // b5 has type MyBool
-)
-
- -

Logical operators

- -

-Logical operators apply to boolean values -and yield a result of the same type as the operands. -The right operand is evaluated conditionally. -

- -
-&&    conditional AND    p && q  is  "if p then q else false"
-||    conditional OR     p || q  is  "if p then true else q"
-!     NOT                !p      is  "not p"
-
- - -

Address operators

- -

-For an operand x of type T, the address operation -&x generates a pointer of type *T to x. -The operand must be addressable, -that is, either a variable, pointer indirection, or slice indexing -operation; or a field selector of an addressable struct operand; -or an array indexing operation of an addressable array. -As an exception to the addressability requirement, x may also be a -(possibly parenthesized) -composite literal. -If the evaluation of x would cause a run-time panic, -then the evaluation of &x does too. -

- -

-For an operand x of pointer type *T, the pointer -indirection *x denotes the variable of type T pointed -to by x. -If x is nil, an attempt to evaluate *x -will cause a run-time panic. -

- -
-&x
-&a[f(2)]
-&Point{2, 3}
-*p
-*pf(x)
-
-var x *int = nil
-*x   // causes a run-time panic
-&*x  // causes a run-time panic
-
- - -

Receive operator

- -

-For an operand ch of channel type, -the value of the receive operation <-ch is the value received -from the channel ch. The channel direction must permit receive operations, -and the type of the receive operation is the element type of the channel. -The expression blocks until a value is available. -Receiving from a nil channel blocks forever. -A receive operation on a closed channel can always proceed -immediately, yielding the element type's zero value -after any previously sent values have been received. -

- -
-v1 := <-ch
-v2 = <-ch
-f(<-ch)
-<-strobe  // wait until clock pulse and discard received value
-
- -

-A receive expression used in an assignment or initialization of the special form -

- -
-x, ok = <-ch
-x, ok := <-ch
-var x, ok = <-ch
-var x, ok T = <-ch
-
- -

-yields an additional untyped boolean result reporting whether the -communication succeeded. The value of ok is true -if the value received was delivered by a successful send operation to the -channel, or false if it is a zero value generated because the -channel is closed and empty. -

- - -

Conversions

- -

-A conversion changes the type of an expression -to the type specified by the conversion. -A conversion may appear literally in the source, or it may be implied -by the context in which an expression appears. -

- -

-An explicit conversion is an expression of the form T(x) -where T is a type and x is an expression -that can be converted to type T. -

- -
-Conversion = Type "(" Expression [ "," ] ")" .
-
- -

-If the type starts with the operator * or <-, -or if the type starts with the keyword func -and has no result list, it must be parenthesized when -necessary to avoid ambiguity: -

- -
-*Point(p)        // same as *(Point(p))
-(*Point)(p)      // p is converted to *Point
-<-chan int(c)    // same as <-(chan int(c))
-(<-chan int)(c)  // c is converted to <-chan int
-func()(x)        // function signature func() x
-(func())(x)      // x is converted to func()
-(func() int)(x)  // x is converted to func() int
-func() int(x)    // x is converted to func() int (unambiguous)
-
- -

-A constant value x can be converted to -type T if x is representable -by a value of T. -As a special case, an integer constant x can be explicitly converted to a -string type using the -same rule -as for non-constant x. -

- -

-Converting a constant yields a typed constant as result. -

- -
-uint(iota)               // iota value of type uint
-float32(2.718281828)     // 2.718281828 of type float32
-complex128(1)            // 1.0 + 0.0i of type complex128
-float32(0.49999999)      // 0.5 of type float32
-float64(-1e-1000)        // 0.0 of type float64
-string('x')              // "x" of type string
-string(0x266c)           // "♬" of type string
-MyString("foo" + "bar")  // "foobar" of type MyString
-string([]byte{'a'})      // not a constant: []byte{'a'} is not a constant
-(*int)(nil)              // not a constant: nil is not a constant, *int is not a boolean, numeric, or string type
-int(1.2)                 // illegal: 1.2 cannot be represented as an int
-string(65.0)             // illegal: 65.0 is not an integer constant
-
- -

-A non-constant value x can be converted to type T -in any of these cases: -

- - - -

-Struct tags are ignored when comparing struct types -for identity for the purpose of conversion: -

- -
-type Person struct {
-	Name    string
-	Address *struct {
-		Street string
-		City   string
-	}
-}
-
-var data *struct {
-	Name    string `json:"name"`
-	Address *struct {
-		Street string `json:"street"`
-		City   string `json:"city"`
-	} `json:"address"`
-}
-
-var person = (*Person)(data)  // ignoring tags, the underlying types are identical
-
- -

-Specific rules apply to (non-constant) conversions between numeric types or -to and from a string type. -These conversions may change the representation of x -and incur a run-time cost. -All other conversions only change the type but not the representation -of x. -

- -

-There is no linguistic mechanism to convert between pointers and integers. -The package unsafe -implements this functionality under -restricted circumstances. -

- -

Conversions between numeric types

- -

-For the conversion of non-constant numeric values, the following rules apply: -

- -
    -
  1. -When converting between integer types, if the value is a signed integer, it is -sign extended to implicit infinite precision; otherwise it is zero extended. -It is then truncated to fit in the result type's size. -For example, if v := uint16(0x10F0), then uint32(int8(v)) == 0xFFFFFFF0. -The conversion always yields a valid value; there is no indication of overflow. -
  2. -
  3. -When converting a floating-point number to an integer, the fraction is discarded -(truncation towards zero). -
  4. -
  5. -When converting an integer or floating-point number to a floating-point type, -or a complex number to another complex type, the result value is rounded -to the precision specified by the destination type. -For instance, the value of a variable x of type float32 -may be stored using additional precision beyond that of an IEEE 754 32-bit number, -but float32(x) represents the result of rounding x's value to -32-bit precision. Similarly, x + 0.1 may use more than 32 bits -of precision, but float32(x + 0.1) does not. -
  6. -
- -

-In all non-constant conversions involving floating-point or complex values, -if the result type cannot represent the value the conversion -succeeds but the result value is implementation-dependent. -

- -

Conversions to and from a string type

- -
    -
  1. -Converting a signed or unsigned integer value to a string type yields a -string containing the UTF-8 representation of the integer. Values outside -the range of valid Unicode code points are converted to "\uFFFD". - -
    -string('a')       // "a"
    -string(-1)        // "\ufffd" == "\xef\xbf\xbd"
    -string(0xf8)      // "\u00f8" == "ø" == "\xc3\xb8"
    -type MyString string
    -MyString(0x65e5)  // "\u65e5" == "日" == "\xe6\x97\xa5"
    -
    -
  2. - -
  3. -Converting a slice of bytes to a string type yields -a string whose successive bytes are the elements of the slice. - -
    -string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'})   // "hellø"
    -string([]byte{})                                     // ""
    -string([]byte(nil))                                  // ""
    -
    -type MyBytes []byte
    -string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'})  // "hellø"
    -
    -
  4. - -
  5. -Converting a slice of runes to a string type yields -a string that is the concatenation of the individual rune values -converted to strings. - -
    -string([]rune{0x767d, 0x9d6c, 0x7fd4})   // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    -string([]rune{})                         // ""
    -string([]rune(nil))                      // ""
    -
    -type MyRunes []rune
    -string(MyRunes{0x767d, 0x9d6c, 0x7fd4})  // "\u767d\u9d6c\u7fd4" == "白鵬翔"
    -
    -
  6. - -
  7. -Converting a value of a string type to a slice of bytes type -yields a slice whose successive elements are the bytes of the string. - -
    -[]byte("hellø")   // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    -[]byte("")        // []byte{}
    -
    -MyBytes("hellø")  // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    -
    -
  8. - -
  9. -Converting a value of a string type to a slice of runes type -yields a slice containing the individual Unicode code points of the string. - -
    -[]rune(MyString("白鵬翔"))  // []rune{0x767d, 0x9d6c, 0x7fd4}
    -[]rune("")                 // []rune{}
    -
    -MyRunes("白鵬翔")           // []rune{0x767d, 0x9d6c, 0x7fd4}
    -
    -
  10. -
- -

Conversions from slice to array pointer

- -

-Converting a slice to an array pointer yields a pointer to the underlying array of the slice. -If the length of the slice is less than the length of the array, -a run-time panic occurs. -

- -
-s := make([]byte, 2, 4)
-s0 := (*[0]byte)(s)      // s0 != nil
-s1 := (*[1]byte)(s[1:])  // &s1[0] == &s[1]
-s2 := (*[2]byte)(s)      // &s2[0] == &s[0]
-s4 := (*[4]byte)(s)      // panics: len([4]byte) > len(s)
-
-var t []string
-t0 := (*[0]string)(t)    // t0 == nil
-t1 := (*[1]string)(t)    // panics: len([1]string) > len(t)
-
-u := make([]byte, 0)
-u0 := (*[0]byte)(u)      // u0 != nil
-
- -

Constant expressions

- -

-Constant expressions may contain only constant -operands and are evaluated at compile time. -

- -

-Untyped boolean, numeric, and string constants may be used as operands -wherever it is legal to use an operand of boolean, numeric, or string type, -respectively. -

- -

-A constant comparison always yields -an untyped boolean constant. If the left operand of a constant -shift expression is an untyped constant, the -result is an integer constant; otherwise it is a constant of the same -type as the left operand, which must be of -integer type. -

- -

-Any other operation on untyped constants results in an untyped constant of the -same kind; that is, a boolean, integer, floating-point, complex, or string -constant. -If the untyped operands of a binary operation (other than a shift) are of -different kinds, the result is of the operand's kind that appears later in this -list: integer, rune, floating-point, complex. -For example, an untyped integer constant divided by an -untyped complex constant yields an untyped complex constant. -

- -
-const a = 2 + 3.0          // a == 5.0   (untyped floating-point constant)
-const b = 15 / 4           // b == 3     (untyped integer constant)
-const c = 15 / 4.0         // c == 3.75  (untyped floating-point constant)
-const Θ float64 = 3/2      // Θ == 1.0   (type float64, 3/2 is integer division)
-const Π float64 = 3/2.     // Π == 1.5   (type float64, 3/2. is float division)
-const d = 1 << 3.0         // d == 8     (untyped integer constant)
-const e = 1.0 << 3         // e == 8     (untyped integer constant)
-const f = int32(1) << 33   // illegal    (constant 8589934592 overflows int32)
-const g = float64(2) >> 1  // illegal    (float64(2) is a typed floating-point constant)
-const h = "foo" > "bar"    // h == true  (untyped boolean constant)
-const j = true             // j == true  (untyped boolean constant)
-const k = 'w' + 1          // k == 'x'   (untyped rune constant)
-const l = "hi"             // l == "hi"  (untyped string constant)
-const m = string(k)        // m == "x"   (type string)
-const Σ = 1 - 0.707i       //            (untyped complex constant)
-const Δ = Σ + 2.0e-4       //            (untyped complex constant)
-const Φ = iota*1i - 1/1i   //            (untyped complex constant)
-
- -

-Applying the built-in function complex to untyped -integer, rune, or floating-point constants yields -an untyped complex constant. -

- -
-const ic = complex(0, c)   // ic == 3.75i  (untyped complex constant)
-const iΘ = complex(0, Θ)   // iΘ == 1i     (type complex128)
-
- -

-Constant expressions are always evaluated exactly; intermediate values and the -constants themselves may require precision significantly larger than supported -by any predeclared type in the language. The following are legal declarations: -

- -
-const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
-const Four int8 = Huge >> 98  // Four == 4                                (type int8)
-
- -

-The divisor of a constant division or remainder operation must not be zero: -

- -
-3.14 / 0.0   // illegal: division by zero
-
- -

-The values of typed constants must always be accurately -representable by values -of the constant type. The following constant expressions are illegal: -

- -
-uint(-1)     // -1 cannot be represented as a uint
-int(3.14)    // 3.14 cannot be represented as an int
-int64(Huge)  // 1267650600228229401496703205376 cannot be represented as an int64
-Four * 300   // operand 300 cannot be represented as an int8 (type of Four)
-Four * 100   // product 400 cannot be represented as an int8 (type of Four)
-
- -

-The mask used by the unary bitwise complement operator ^ matches -the rule for non-constants: the mask is all 1s for unsigned constants -and -1 for signed and untyped constants. -

- -
-^1         // untyped integer constant, equal to -2
-uint8(^1)  // illegal: same as uint8(-2), -2 cannot be represented as a uint8
-^uint8(1)  // typed uint8 constant, same as 0xFF ^ uint8(1) = uint8(0xFE)
-int8(^1)   // same as int8(-2)
-^int8(1)   // same as -1 ^ int8(1) = -2
-
- -

-Implementation restriction: A compiler may use rounding while -computing untyped floating-point or complex constant expressions; see -the implementation restriction in the section -on constants. This rounding may cause a -floating-point constant expression to be invalid in an integer -context, even if it would be integral when calculated using infinite -precision, and vice versa. -

- - -

Order of evaluation

- -

-At package level, initialization dependencies -determine the evaluation order of individual initialization expressions in -variable declarations. -Otherwise, when evaluating the operands of an -expression, assignment, or -return statement, -all function calls, method calls, and -communication operations are evaluated in lexical left-to-right -order. -

- -

-For example, in the (function-local) assignment -

-
-y[f()], ok = g(h(), i()+x[j()], <-c), k()
-
-

-the function calls and communication happen in the order -f(), h(), i(), j(), -<-c, g(), and k(). -However, the order of those events compared to the evaluation -and indexing of x and the evaluation -of y is not specified. -

- -
-a := 1
-f := func() int { a++; return a }
-x := []int{a, f()}            // x may be [1, 2] or [2, 2]: evaluation order between a and f() is not specified
-m := map[int]int{a: 1, a: 2}  // m may be {2: 1} or {2: 2}: evaluation order between the two map assignments is not specified
-n := map[int]int{a: f()}      // n may be {2: 3} or {3: 3}: evaluation order between the key and the value is not specified
-
- -

-At package level, initialization dependencies override the left-to-right rule -for individual initialization expressions, but not for operands within each -expression: -

- -
-var a, b, c = f() + v(), g(), sqr(u()) + v()
-
-func f() int        { return c }
-func g() int        { return a }
-func sqr(x int) int { return x*x }
-
-// functions u and v are independent of all other variables and functions
-
- -

-The function calls happen in the order -u(), sqr(), v(), -f(), v(), and g(). -

- -

-Floating-point operations within a single expression are evaluated according to -the associativity of the operators. Explicit parentheses affect the evaluation -by overriding the default associativity. -In the expression x + (y + z) the addition y + z -is performed before adding x. -

- -

Statements

- -

-Statements control execution. -

- -
-Statement =
-	Declaration | LabeledStmt | SimpleStmt |
-	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
-	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
-	DeferStmt .
-
-SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
-
- -

Terminating statements

- -

-A terminating statement interrupts the regular flow of control in -a block. The following statements are terminating: -

- -
    -
  1. - A "return" or - "goto" statement. - - -
  2. - -
  3. - A call to the built-in function - panic. - - -
  4. - -
  5. - A block in which the statement list ends in a terminating statement. - - -
  6. - -
  7. - An "if" statement in which: - -
  8. - -
  9. - A "for" statement in which: - -
  10. - -
  11. - A "switch" statement in which: - -
  12. - -
  13. - A "select" statement in which: - -
  14. - -
  15. - A labeled statement labeling - a terminating statement. -
  16. -
- -

-All other statements are not terminating. -

- -

-A statement list ends in a terminating statement if the list -is not empty and its final non-empty statement is terminating. -

- - -

Empty statements

- -

-The empty statement does nothing. -

- -
-EmptyStmt = .
-
- - -

Labeled statements

- -

-A labeled statement may be the target of a goto, -break or continue statement. -

- -
-LabeledStmt = Label ":" Statement .
-Label       = identifier .
-
- -
-Error: log.Panic("error encountered")
-
- - -

Expression statements

- -

-With the exception of specific built-in functions, -function and method calls and -receive operations -can appear in statement context. Such statements may be parenthesized. -

- -
-ExpressionStmt = Expression .
-
- -

-The following built-in functions are not permitted in statement context: -

- -
-append cap complex imag len make new real
-unsafe.Add unsafe.Alignof unsafe.Offsetof unsafe.Sizeof unsafe.Slice
-
- -
-h(x+y)
-f.Close()
-<-ch
-(<-ch)
-len("foo")  // illegal if len is the built-in function
-
- - -

Send statements

- -

-A send statement sends a value on a channel. -The channel expression must be of channel type, -the channel direction must permit send operations, -and the type of the value to be sent must be assignable -to the channel's element type. -

- -
-SendStmt = Channel "<-" Expression .
-Channel  = Expression .
-
- -

-Both the channel and the value expression are evaluated before communication -begins. Communication blocks until the send can proceed. -A send on an unbuffered channel can proceed if a receiver is ready. -A send on a buffered channel can proceed if there is room in the buffer. -A send on a closed channel proceeds by causing a run-time panic. -A send on a nil channel blocks forever. -

- -
-ch <- 3  // send value 3 to channel ch
-
- - -

IncDec statements

- -

-The "++" and "--" statements increment or decrement their operands -by the untyped constant 1. -As with an assignment, the operand must be addressable -or a map index expression. -

- -
-IncDecStmt = Expression ( "++" | "--" ) .
-
- -

-The following assignment statements are semantically -equivalent: -

- -
-IncDec statement    Assignment
-x++                 x += 1
-x--                 x -= 1
-
- - -

Assignments

- -
-Assignment = ExpressionList assign_op ExpressionList .
-
-assign_op = [ add_op | mul_op ] "=" .
-
- -

-Each left-hand side operand must be addressable, -a map index expression, or (for = assignments only) the -blank identifier. -Operands may be parenthesized. -

- -
-x = 1
-*p = f()
-a[i] = 23
-(k) = <-ch  // same as: k = <-ch
-
- -

-An assignment operation x op= -y where op is a binary arithmetic operator -is equivalent to x = x op -(y) but evaluates x -only once. The op= construct is a single token. -In assignment operations, both the left- and right-hand expression lists -must contain exactly one single-valued expression, and the left-hand -expression must not be the blank identifier. -

- -
-a[i] <<= 2
-i &^= 1<<n
-
- -

-A tuple assignment assigns the individual elements of a multi-valued -operation to a list of variables. There are two forms. In the -first, the right hand operand is a single multi-valued expression -such as a function call, a channel or -map operation, or a type assertion. -The number of operands on the left -hand side must match the number of values. For instance, if -f is a function returning two values, -

- -
-x, y = f()
-
- -

-assigns the first value to x and the second to y. -In the second form, the number of operands on the left must equal the number -of expressions on the right, each of which must be single-valued, and the -nth expression on the right is assigned to the nth -operand on the left: -

- -
-one, two, three = '一', '二', '三'
-
- -

-The blank identifier provides a way to -ignore right-hand side values in an assignment: -

- -
-_ = x       // evaluate x but ignore it
-x, _ = f()  // evaluate f() but ignore second result value
-
- -

-The assignment proceeds in two phases. -First, the operands of index expressions -and pointer indirections -(including implicit pointer indirections in selectors) -on the left and the expressions on the right are all -evaluated in the usual order. -Second, the assignments are carried out in left-to-right order. -

- -
-a, b = b, a  // exchange a and b
-
-x := []int{1, 2, 3}
-i := 0
-i, x[i] = 1, 2  // set i = 1, x[0] = 2
-
-i = 0
-x[i], i = 2, 1  // set x[0] = 2, i = 1
-
-x[0], x[0] = 1, 2  // set x[0] = 1, then x[0] = 2 (so x[0] == 2 at end)
-
-x[1], x[3] = 4, 5  // set x[1] = 4, then panic setting x[3] = 5.
-
-type Point struct { x, y int }
-var p *Point
-x[2], p.x = 6, 7  // set x[2] = 6, then panic setting p.x = 7
-
-i = 2
-x = []int{3, 5, 7}
-for i, x[i] = range x {  // set i, x[2] = 0, x[0]
-	break
-}
-// after this loop, i == 0 and x == []int{3, 5, 3}
-
- -

-In assignments, each value must be assignable -to the type of the operand to which it is assigned, with the following special cases: -

- -
    -
  1. - Any typed value may be assigned to the blank identifier. -
  2. - -
  3. - If an untyped constant - is assigned to a variable of interface type or the blank identifier, - the constant is first implicitly converted to its - default type. -
  4. - -
  5. - If an untyped boolean value is assigned to a variable of interface type or - the blank identifier, it is first implicitly converted to type bool. -
  6. -
- -

If statements

- -

-"If" statements specify the conditional execution of two branches -according to the value of a boolean expression. If the expression -evaluates to true, the "if" branch is executed, otherwise, if -present, the "else" branch is executed. -

- -
-IfStmt = "if" [ SimpleStmt ";" ] Expression Block [ "else" ( IfStmt | Block ) ] .
-
- -
-if x > max {
-	x = max
-}
-
- -

-The expression may be preceded by a simple statement, which -executes before the expression is evaluated. -

- -
-if x := f(); x < y {
-	return x
-} else if x > z {
-	return z
-} else {
-	return y
-}
-
- - -

Switch statements

- -

-"Switch" statements provide multi-way execution. -An expression or type is compared to the "cases" -inside the "switch" to determine which branch -to execute. -

- -
-SwitchStmt = ExprSwitchStmt | TypeSwitchStmt .
-
- -

-There are two forms: expression switches and type switches. -In an expression switch, the cases contain expressions that are compared -against the value of the switch expression. -In a type switch, the cases contain types that are compared against the -type of a specially annotated switch expression. -The switch expression is evaluated exactly once in a switch statement. -

- -

Expression switches

- -

-In an expression switch, -the switch expression is evaluated and -the case expressions, which need not be constants, -are evaluated left-to-right and top-to-bottom; the first one that equals the -switch expression -triggers execution of the statements of the associated case; -the other cases are skipped. -If no case matches and there is a "default" case, -its statements are executed. -There can be at most one default case and it may appear anywhere in the -"switch" statement. -A missing switch expression is equivalent to the boolean value -true. -

- -
-ExprSwitchStmt = "switch" [ SimpleStmt ";" ] [ Expression ] "{" { ExprCaseClause } "}" .
-ExprCaseClause = ExprSwitchCase ":" StatementList .
-ExprSwitchCase = "case" ExpressionList | "default" .
-
- -

-If the switch expression evaluates to an untyped constant, it is first implicitly -converted to its default type. -The predeclared untyped value nil cannot be used as a switch expression. -The switch expression type must be comparable. -

- -

-If a case expression is untyped, it is first implicitly converted -to the type of the switch expression. -For each (possibly converted) case expression x and the value t -of the switch expression, x == t must be a valid comparison. -

- -

-In other words, the switch expression is treated as if it were used to declare and -initialize a temporary variable t without explicit type; it is that -value of t against which each case expression x is tested -for equality. -

- -

-In a case or default clause, the last non-empty statement -may be a (possibly labeled) -"fallthrough" statement to -indicate that control should flow from the end of this clause to -the first statement of the next clause. -Otherwise control flows to the end of the "switch" statement. -A "fallthrough" statement may appear as the last statement of all -but the last clause of an expression switch. -

- -

-The switch expression may be preceded by a simple statement, which -executes before the expression is evaluated. -

- -
-switch tag {
-default: s3()
-case 0, 1, 2, 3: s1()
-case 4, 5, 6, 7: s2()
-}
-
-switch x := f(); {  // missing switch expression means "true"
-case x < 0: return -x
-default: return x
-}
-
-switch {
-case x < y: f1()
-case x < z: f2()
-case x == 4: f3()
-}
-
- -

-Implementation restriction: A compiler may disallow multiple case -expressions evaluating to the same constant. -For instance, the current compilers disallow duplicate integer, -floating point, or string constants in case expressions. -

- -

Type switches

- -

-A type switch compares types rather than values. It is otherwise similar -to an expression switch. It is marked by a special switch expression that -has the form of a type assertion -using the keyword type rather than an actual type: -

- -
-switch x.(type) {
-// cases
-}
-
- -

-Cases then match actual types T against the dynamic type of the -expression x. As with type assertions, x must be of -interface type, and each non-interface type -T listed in a case must implement the type of x. -The types listed in the cases of a type switch must all be -different. -

- -
-TypeSwitchStmt  = "switch" [ SimpleStmt ";" ] TypeSwitchGuard "{" { TypeCaseClause } "}" .
-TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
-TypeCaseClause  = TypeSwitchCase ":" StatementList .
-TypeSwitchCase  = "case" TypeList | "default" .
-TypeList        = Type { "," Type } .
-
- -

-The TypeSwitchGuard may include a -short variable declaration. -When that form is used, the variable is declared at the end of the -TypeSwitchCase in the implicit block of each clause. -In clauses with a case listing exactly one type, the variable -has that type; otherwise, the variable has the type of the expression -in the TypeSwitchGuard. -

- -

-Instead of a type, a case may use the predeclared identifier -nil; -that case is selected when the expression in the TypeSwitchGuard -is a nil interface value. -There may be at most one nil case. -

- -

-Given an expression x of type interface{}, -the following type switch: -

- -
-switch i := x.(type) {
-case nil:
-	printString("x is nil")                // type of i is type of x (interface{})
-case int:
-	printInt(i)                            // type of i is int
-case float64:
-	printFloat64(i)                        // type of i is float64
-case func(int) float64:
-	printFunction(i)                       // type of i is func(int) float64
-case bool, string:
-	printString("type is bool or string")  // type of i is type of x (interface{})
-default:
-	printString("don't know the type")     // type of i is type of x (interface{})
-}
-
- -

-could be rewritten: -

- -
-v := x  // x is evaluated exactly once
-if v == nil {
-	i := v                                 // type of i is type of x (interface{})
-	printString("x is nil")
-} else if i, isInt := v.(int); isInt {
-	printInt(i)                            // type of i is int
-} else if i, isFloat64 := v.(float64); isFloat64 {
-	printFloat64(i)                        // type of i is float64
-} else if i, isFunc := v.(func(int) float64); isFunc {
-	printFunction(i)                       // type of i is func(int) float64
-} else {
-	_, isBool := v.(bool)
-	_, isString := v.(string)
-	if isBool || isString {
-		i := v                         // type of i is type of x (interface{})
-		printString("type is bool or string")
-	} else {
-		i := v                         // type of i is type of x (interface{})
-		printString("don't know the type")
-	}
-}
-
- -

-The type switch guard may be preceded by a simple statement, which -executes before the guard is evaluated. -

- -

-The "fallthrough" statement is not permitted in a type switch. -

- -

For statements

- -

-A "for" statement specifies repeated execution of a block. There are three forms: -The iteration may be controlled by a single condition, a "for" clause, or a "range" clause. -

- -
-ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
-Condition = Expression .
-
- -

For statements with single condition

- -

-In its simplest form, a "for" statement specifies the repeated execution of -a block as long as a boolean condition evaluates to true. -The condition is evaluated before each iteration. -If the condition is absent, it is equivalent to the boolean value -true. -

- -
-for a < b {
-	a *= 2
-}
-
- -

For statements with for clause

- -

-A "for" statement with a ForClause is also controlled by its condition, but -additionally it may specify an init -and a post statement, such as an assignment, -an increment or decrement statement. The init statement may be a -short variable declaration, but the post statement must not. -Variables declared by the init statement are re-used in each iteration. -

- -
-ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
-InitStmt = SimpleStmt .
-PostStmt = SimpleStmt .
-
- -
-for i := 0; i < 10; i++ {
-	f(i)
-}
-
- -

-If non-empty, the init statement is executed once before evaluating the -condition for the first iteration; -the post statement is executed after each execution of the block (and -only if the block was executed). -Any element of the ForClause may be empty but the -semicolons are -required unless there is only a condition. -If the condition is absent, it is equivalent to the boolean value -true. -

- -
-for cond { S() }    is the same as    for ; cond ; { S() }
-for      { S() }    is the same as    for true     { S() }
-
- -

For statements with range clause

- -

-A "for" statement with a "range" clause -iterates through all entries of an array, slice, string or map, -or values received on a channel. For each entry it assigns iteration values -to corresponding iteration variables if present and then executes the block. -

- -
-RangeClause = [ ExpressionList "=" | IdentifierList ":=" ] "range" Expression .
-
- -

-The expression on the right in the "range" clause is called the range expression, -which may be an array, pointer to an array, slice, string, map, or channel permitting -receive operations. -As with an assignment, if present the operands on the left must be -addressable or map index expressions; they -denote the iteration variables. If the range expression is a channel, at most -one iteration variable is permitted, otherwise there may be up to two. -If the last iteration variable is the blank identifier, -the range clause is equivalent to the same clause without that identifier. -

- -

-The range expression x is evaluated once before beginning the loop, -with one exception: if at most one iteration variable is present and -len(x) is constant, -the range expression is not evaluated. -

- -

-Function calls on the left are evaluated once per iteration. -For each iteration, iteration values are produced as follows -if the respective iteration variables are present: -

- -
-Range expression                          1st value          2nd value
-
-array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
-string          s  string type            index    i  int    see below  rune
-map             m  map[K]V                key      k  K      m[k]       V
-channel         c  chan E, <-chan E       element  e  E
-
- -
    -
  1. -For an array, pointer to array, or slice value a, the index iteration -values are produced in increasing order, starting at element index 0. -If at most one iteration variable is present, the range loop produces -iteration values from 0 up to len(a)-1 and does not index into the array -or slice itself. For a nil slice, the number of iterations is 0. -
  2. - -
  3. -For a string value, the "range" clause iterates over the Unicode code points -in the string starting at byte index 0. On successive iterations, the index value will be the -index of the first byte of successive UTF-8-encoded code points in the string, -and the second value, of type rune, will be the value of -the corresponding code point. If the iteration encounters an invalid -UTF-8 sequence, the second value will be 0xFFFD, -the Unicode replacement character, and the next iteration will advance -a single byte in the string. -
  4. - -
  5. -The iteration order over maps is not specified -and is not guaranteed to be the same from one iteration to the next. -If a map entry that has not yet been reached is removed during iteration, -the corresponding iteration value will not be produced. If a map entry is -created during iteration, that entry may be produced during the iteration or -may be skipped. The choice may vary for each entry created and from one -iteration to the next. -If the map is nil, the number of iterations is 0. -
  6. - -
  7. -For channels, the iteration values produced are the successive values sent on -the channel until the channel is closed. If the channel -is nil, the range expression blocks forever. -
  8. -
- -

-The iteration values are assigned to the respective -iteration variables as in an assignment statement. -

- -

-The iteration variables may be declared by the "range" clause using a form of -short variable declaration -(:=). -In this case their types are set to the types of the respective iteration values -and their scope is the block of the "for" -statement; they are re-used in each iteration. -If the iteration variables are declared outside the "for" statement, -after execution their values will be those of the last iteration. -

- -
-var testdata *struct {
-	a *[7]int
-}
-for i, _ := range testdata.a {
-	// testdata.a is never evaluated; len(testdata.a) is constant
-	// i ranges from 0 to 6
-	f(i)
-}
-
-var a [10]string
-for i, s := range a {
-	// type of i is int
-	// type of s is string
-	// s == a[i]
-	g(i, s)
-}
-
-var key string
-var val interface{}  // element type of m is assignable to val
-m := map[string]int{"mon":0, "tue":1, "wed":2, "thu":3, "fri":4, "sat":5, "sun":6}
-for key, val = range m {
-	h(key, val)
-}
-// key == last map key encountered in iteration
-// val == map[key]
-
-var ch chan Work = producer()
-for w := range ch {
-	doWork(w)
-}
-
-// empty a channel
-for range ch {}
-
- - -

Go statements

- -

-A "go" statement starts the execution of a function call -as an independent concurrent thread of control, or goroutine, -within the same address space. -

- -
-GoStmt = "go" Expression .
-
- -

-The expression must be a function or method call; it cannot be parenthesized. -Calls of built-in functions are restricted as for -expression statements. -

- -

-The function value and parameters are -evaluated as usual -in the calling goroutine, but -unlike with a regular call, program execution does not wait -for the invoked function to complete. -Instead, the function begins executing independently -in a new goroutine. -When the function terminates, its goroutine also terminates. -If the function has any return values, they are discarded when the -function completes. -

- -
-go Server()
-go func(ch chan<- bool) { for { sleep(10); ch <- true }} (c)
-
- - -

Select statements

- -

-A "select" statement chooses which of a set of possible -send or -receive -operations will proceed. -It looks similar to a -"switch" statement but with the -cases all referring to communication operations. -

- -
-SelectStmt = "select" "{" { CommClause } "}" .
-CommClause = CommCase ":" StatementList .
-CommCase   = "case" ( SendStmt | RecvStmt ) | "default" .
-RecvStmt   = [ ExpressionList "=" | IdentifierList ":=" ] RecvExpr .
-RecvExpr   = Expression .
-
- -

-A case with a RecvStmt may assign the result of a RecvExpr to one or -two variables, which may be declared using a -short variable declaration. -The RecvExpr must be a (possibly parenthesized) receive operation. -There can be at most one default case and it may appear anywhere -in the list of cases. -

- -

-Execution of a "select" statement proceeds in several steps: -

- -
    -
  1. -For all the cases in the statement, the channel operands of receive operations -and the channel and right-hand-side expressions of send statements are -evaluated exactly once, in source order, upon entering the "select" statement. -The result is a set of channels to receive from or send to, -and the corresponding values to send. -Any side effects in that evaluation will occur irrespective of which (if any) -communication operation is selected to proceed. -Expressions on the left-hand side of a RecvStmt with a short variable declaration -or assignment are not yet evaluated. -
  2. - -
  3. -If one or more of the communications can proceed, -a single one that can proceed is chosen via a uniform pseudo-random selection. -Otherwise, if there is a default case, that case is chosen. -If there is no default case, the "select" statement blocks until -at least one of the communications can proceed. -
  4. - -
  5. -Unless the selected case is the default case, the respective communication -operation is executed. -
  6. - -
  7. -If the selected case is a RecvStmt with a short variable declaration or -an assignment, the left-hand side expressions are evaluated and the -received value (or values) are assigned. -
  8. - -
  9. -The statement list of the selected case is executed. -
  10. -
- -

-Since communication on nil channels can never proceed, -a select with only nil channels and no default case blocks forever. -

- -
-var a []int
-var c, c1, c2, c3, c4 chan int
-var i1, i2 int
-select {
-case i1 = <-c1:
-	print("received ", i1, " from c1\n")
-case c2 <- i2:
-	print("sent ", i2, " to c2\n")
-case i3, ok := (<-c3):  // same as: i3, ok := <-c3
-	if ok {
-		print("received ", i3, " from c3\n")
-	} else {
-		print("c3 is closed\n")
-	}
-case a[f()] = <-c4:
-	// same as:
-	// case t := <-c4
-	//	a[f()] = t
-default:
-	print("no communication\n")
-}
-
-for {  // send random sequence of bits to c
-	select {
-	case c <- 0:  // note: no statement, no fallthrough, no folding of cases
-	case c <- 1:
-	}
-}
-
-select {}  // block forever
-
- - -

Return statements

- -

-A "return" statement in a function F terminates the execution -of F, and optionally provides one or more result values. -Any functions deferred by F -are executed before F returns to its caller. -

- -
-ReturnStmt = "return" [ ExpressionList ] .
-
- -

-In a function without a result type, a "return" statement must not -specify any result values. -

-
-func noResult() {
-	return
-}
-
- -

-There are three ways to return values from a function with a result -type: -

- -
    -
  1. The return value or values may be explicitly listed - in the "return" statement. Each expression must be single-valued - and assignable - to the corresponding element of the function's result type. -
    -func simpleF() int {
    -	return 2
    -}
    -
    -func complexF1() (re float64, im float64) {
    -	return -7.0, -4.0
    -}
    -
    -
  2. -
  3. The expression list in the "return" statement may be a single - call to a multi-valued function. The effect is as if each value - returned from that function were assigned to a temporary - variable with the type of the respective value, followed by a - "return" statement listing these variables, at which point the - rules of the previous case apply. -
    -func complexF2() (re float64, im float64) {
    -	return complexF1()
    -}
    -
    -
  4. -
  5. The expression list may be empty if the function's result - type specifies names for its result parameters. - The result parameters act as ordinary local variables - and the function may assign values to them as necessary. - The "return" statement returns the values of these variables. -
    -func complexF3() (re float64, im float64) {
    -	re = 7.0
    -	im = 4.0
    -	return
    -}
    -
    -func (devnull) Write(p []byte) (n int, _ error) {
    -	n = len(p)
    -	return
    -}
    -
    -
  6. -
- -

-Regardless of how they are declared, all the result values are initialized to -the zero values for their type upon entry to the -function. A "return" statement that specifies results sets the result parameters before -any deferred functions are executed. -

- -

-Implementation restriction: A compiler may disallow an empty expression list -in a "return" statement if a different entity (constant, type, or variable) -with the same name as a result parameter is in -scope at the place of the return. -

- -
-func f(n int) (res int, err error) {
-	if _, err := f(n-1); err != nil {
-		return  // invalid return statement: err is shadowed
-	}
-	return
-}
-
- -

Break statements

- -

-A "break" statement terminates execution of the innermost -"for", -"switch", or -"select" statement -within the same function. -

- -
-BreakStmt = "break" [ Label ] .
-
- -

-If there is a label, it must be that of an enclosing -"for", "switch", or "select" statement, -and that is the one whose execution terminates. -

- -
-OuterLoop:
-	for i = 0; i < n; i++ {
-		for j = 0; j < m; j++ {
-			switch a[i][j] {
-			case nil:
-				state = Error
-				break OuterLoop
-			case item:
-				state = Found
-				break OuterLoop
-			}
-		}
-	}
-
- -

Continue statements

- -

-A "continue" statement begins the next iteration of the -innermost "for" loop at its post statement. -The "for" loop must be within the same function. -

- -
-ContinueStmt = "continue" [ Label ] .
-
- -

-If there is a label, it must be that of an enclosing -"for" statement, and that is the one whose execution -advances. -

- -
-RowLoop:
-	for y, row := range rows {
-		for x, data := range row {
-			if data == endOfRow {
-				continue RowLoop
-			}
-			row[x] = data + bias(x, y)
-		}
-	}
-
- -

Goto statements

- -

-A "goto" statement transfers control to the statement with the corresponding label -within the same function. -

- -
-GotoStmt = "goto" Label .
-
- -
-goto Error
-
- -

-Executing the "goto" statement must not cause any variables to come into -scope that were not already in scope at the point of the goto. -For instance, this example: -

- -
-	goto L  // BAD
-	v := 3
-L:
-
- -

-is erroneous because the jump to label L skips -the creation of v. -

- -

-A "goto" statement outside a block cannot jump to a label inside that block. -For instance, this example: -

- -
-if n%2 == 1 {
-	goto L1
-}
-for n > 0 {
-	f()
-	n--
-L1:
-	f()
-	n--
-}
-
- -

-is erroneous because the label L1 is inside -the "for" statement's block but the goto is not. -

- -

Fallthrough statements

- -

-A "fallthrough" statement transfers control to the first statement of the -next case clause in an expression "switch" statement. -It may be used only as the final non-empty statement in such a clause. -

- -
-FallthroughStmt = "fallthrough" .
-
- - -

Defer statements

- -

-A "defer" statement invokes a function whose execution is deferred -to the moment the surrounding function returns, either because the -surrounding function executed a return statement, -reached the end of its function body, -or because the corresponding goroutine is panicking. -

- -
-DeferStmt = "defer" Expression .
-
- -

-The expression must be a function or method call; it cannot be parenthesized. -Calls of built-in functions are restricted as for -expression statements. -

- -

-Each time a "defer" statement -executes, the function value and parameters to the call are -evaluated as usual -and saved anew but the actual function is not invoked. -Instead, deferred functions are invoked immediately before -the surrounding function returns, in the reverse order -they were deferred. That is, if the surrounding function -returns through an explicit return statement, -deferred functions are executed after any result parameters are set -by that return statement but before the function returns to its caller. -If a deferred function value evaluates -to nil, execution panics -when the function is invoked, not when the "defer" statement is executed. -

- -

-For instance, if the deferred function is -a function literal and the surrounding -function has named result parameters that -are in scope within the literal, the deferred function may access and modify -the result parameters before they are returned. -If the deferred function has any return values, they are discarded when -the function completes. -(See also the section on handling panics.) -

- -
-lock(l)
-defer unlock(l)  // unlocking happens before surrounding function returns
-
-// prints 3 2 1 0 before surrounding function returns
-for i := 0; i <= 3; i++ {
-	defer fmt.Print(i)
-}
-
-// f returns 42
-func f() (result int) {
-	defer func() {
-		// result is accessed after it was set to 6 by the return statement
-		result *= 7
-	}()
-	return 6
-}
-
- -

Built-in functions

- -

-Built-in functions are -predeclared. -They are called like any other function but some of them -accept a type instead of an expression as the first argument. -

- -

-The built-in functions do not have standard Go types, -so they can only appear in call expressions; -they cannot be used as function values. -

- -

Close

- -

-For a channel c, the built-in function close(c) -records that no more values will be sent on the channel. -It is an error if c is a receive-only channel. -Sending to or closing a closed channel causes a run-time panic. -Closing the nil channel also causes a run-time panic. -After calling close, and after any previously -sent values have been received, receive operations will return -the zero value for the channel's type without blocking. -The multi-valued receive operation -returns a received value along with an indication of whether the channel is closed. -

- - -

Length and capacity

- -

-The built-in functions len and cap take arguments -of various types and return a result of type int. -The implementation guarantees that the result always fits into an int. -

- -
-Call      Argument type    Result
-
-len(s)    string type      string length in bytes
-          [n]T, *[n]T      array length (== n)
-          []T              slice length
-          map[K]T          map length (number of defined keys)
-          chan T           number of elements queued in channel buffer
-
-cap(s)    [n]T, *[n]T      array length (== n)
-          []T              slice capacity
-          chan T           channel buffer capacity
-
- -

-The capacity of a slice is the number of elements for which there is -space allocated in the underlying array. -At any time the following relationship holds: -

- -
-0 <= len(s) <= cap(s)
-
- -

-The length of a nil slice, map or channel is 0. -The capacity of a nil slice or channel is 0. -

- -

-The expression len(s) is constant if -s is a string constant. The expressions len(s) and -cap(s) are constants if the type of s is an array -or pointer to an array and the expression s does not contain -channel receives or (non-constant) -function calls; in this case s is not evaluated. -Otherwise, invocations of len and cap are not -constant and s is evaluated. -

- -
-const (
-	c1 = imag(2i)                    // imag(2i) = 2.0 is a constant
-	c2 = len([10]float64{2})         // [10]float64{2} contains no function calls
-	c3 = len([10]float64{c1})        // [10]float64{c1} contains no function calls
-	c4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issued
-	c5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call
-)
-var z complex128
-
- -

Allocation

- -

-The built-in function new takes a type T, -allocates storage for a variable of that type -at run time, and returns a value of type *T -pointing to it. -The variable is initialized as described in the section on -initial values. -

- -
-new(T)
-
- -

-For instance -

- -
-type S struct { a int; b float64 }
-new(S)
-
- -

-allocates storage for a variable of type S, -initializes it (a=0, b=0.0), -and returns a value of type *S containing the address -of the location. -

- -

Making slices, maps and channels

- -

-The built-in function make takes a type T, -which must be a slice, map or channel type, -optionally followed by a type-specific list of expressions. -It returns a value of type T (not *T). -The memory is initialized as described in the section on -initial values. -

- -
-Call             Type T     Result
-
-make(T, n)       slice      slice of type T with length n and capacity n
-make(T, n, m)    slice      slice of type T with length n and capacity m
-
-make(T)          map        map of type T
-make(T, n)       map        map of type T with initial space for approximately n elements
-
-make(T)          channel    unbuffered channel of type T
-make(T, n)       channel    buffered channel of type T, buffer size n
-
- - -

-Each of the size arguments n and m must be of integer type -or an untyped constant. -A constant size argument must be non-negative and representable -by a value of type int; if it is an untyped constant it is given type int. -If both n and m are provided and are constant, then -n must be no larger than m. -If n is negative or larger than m at run time, -a run-time panic occurs. -

- -
-s := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100
-s := make([]int, 1e3)           // slice with len(s) == cap(s) == 1000
-s := make([]int, 1<<63)         // illegal: len(s) is not representable by a value of type int
-s := make([]int, 10, 0)         // illegal: len(s) > cap(s)
-c := make(chan int, 10)         // channel with a buffer size of 10
-m := make(map[string]int, 100)  // map with initial space for approximately 100 elements
-
- -

-Calling make with a map type and size hint n will -create a map with initial space to hold n map elements. -The precise behavior is implementation-dependent. -

- - -

Appending to and copying slices

- -

-The built-in functions append and copy assist in -common slice operations. -For both functions, the result is independent of whether the memory referenced -by the arguments overlaps. -

- -

-The variadic function append -appends zero or more values x -to s of type S, which must be a slice type, and -returns the resulting slice, also of type S. -The values x are passed to a parameter of type ...T -where T is the element type of -S and the respective -parameter passing rules apply. -As a special case, append also accepts a first argument -assignable to type []byte with a second argument of -string type followed by .... This form appends the -bytes of the string. -

- -
-append(s S, x ...T) S  // T is the element type of S
-
- -

-If the capacity of s is not large enough to fit the additional -values, append allocates a new, sufficiently large underlying -array that fits both the existing slice elements and the additional values. -Otherwise, append re-uses the underlying array. -

- -
-s0 := []int{0, 0}
-s1 := append(s0, 2)                // append a single element     s1 == []int{0, 0, 2}
-s2 := append(s1, 3, 5, 7)          // append multiple elements    s2 == []int{0, 0, 2, 3, 5, 7}
-s3 := append(s2, s0...)            // append a slice              s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
-s4 := append(s3[3:6], s3[2:]...)   // append overlapping slice    s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
-
-var t []interface{}
-t = append(t, 42, 3.1415, "foo")   //                             t == []interface{}{42, 3.1415, "foo"}
-
-var b []byte
-b = append(b, "bar"...)            // append string contents      b == []byte{'b', 'a', 'r' }
-
- -

-The function copy copies slice elements from -a source src to a destination dst and returns the -number of elements copied. -Both arguments must have identical element type T and must be -assignable to a slice of type []T. -The number of elements copied is the minimum of -len(src) and len(dst). -As a special case, copy also accepts a destination argument assignable -to type []byte with a source argument of a string type. -This form copies the bytes from the string into the byte slice. -

- -
-copy(dst, src []T) int
-copy(dst []byte, src string) int
-
- -

-Examples: -

- -
-var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
-var s = make([]int, 6)
-var b = make([]byte, 5)
-n1 := copy(s, a[0:])            // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
-n2 := copy(s, s[2:])            // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
-n3 := copy(b, "Hello, World!")  // n3 == 5, b == []byte("Hello")
-
- - -

Deletion of map elements

- -

-The built-in function delete removes the element with key -k from a map m. The -type of k must be assignable -to the key type of m. -

- -
-delete(m, k)  // remove element m[k] from map m
-
- -

-If the map m is nil or the element m[k] -does not exist, delete is a no-op. -

- - -

Manipulating complex numbers

- -

-Three functions assemble and disassemble complex numbers. -The built-in function complex constructs a complex -value from a floating-point real and imaginary part, while -real and imag -extract the real and imaginary parts of a complex value. -

- -
-complex(realPart, imaginaryPart floatT) complexT
-real(complexT) floatT
-imag(complexT) floatT
-
- -

-The type of the arguments and return value correspond. -For complex, the two arguments must be of the same -floating-point type and the return type is the complex type -with the corresponding floating-point constituents: -complex64 for float32 arguments, and -complex128 for float64 arguments. -If one of the arguments evaluates to an untyped constant, it is first implicitly -converted to the type of the other argument. -If both arguments evaluate to untyped constants, they must be non-complex -numbers or their imaginary parts must be zero, and the return value of -the function is an untyped complex constant. -

- -

-For real and imag, the argument must be -of complex type, and the return type is the corresponding floating-point -type: float32 for a complex64 argument, and -float64 for a complex128 argument. -If the argument evaluates to an untyped constant, it must be a number, -and the return value of the function is an untyped floating-point constant. -

- -

-The real and imag functions together form the inverse of -complex, so for a value z of a complex type Z, -z == Z(complex(real(z), imag(z))). -

- -

-If the operands of these functions are all constants, the return -value is a constant. -

- -
-var a = complex(2, -2)             // complex128
-const b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i
-x := float32(math.Cos(math.Pi/2))  // float32
-var c64 = complex(5, -x)           // complex64
-var s int = complex(1, 0)          // untyped complex constant 1 + 0i can be converted to int
-_ = complex(1, 2<<s)               // illegal: 2 assumes floating-point type, cannot shift
-var rl = real(c64)                 // float32
-var im = imag(a)                   // float64
-const c = imag(b)                  // untyped constant -1.4
-_ = imag(3 << s)                   // illegal: 3 assumes complex type, cannot shift
-
- -

Handling panics

- -

Two built-in functions, panic and recover, -assist in reporting and handling run-time panics -and program-defined error conditions. -

- -
-func panic(interface{})
-func recover() interface{}
-
- -

-While executing a function F, -an explicit call to panic or a run-time panic -terminates the execution of F. -Any functions deferred by F -are then executed as usual. -Next, any deferred functions run by F's caller are run, -and so on up to any deferred by the top-level function in the executing goroutine. -At that point, the program is terminated and the error -condition is reported, including the value of the argument to panic. -This termination sequence is called panicking. -

- -
-panic(42)
-panic("unreachable")
-panic(Error("cannot parse"))
-
- -

-The recover function allows a program to manage behavior -of a panicking goroutine. -Suppose a function G defers a function D that calls -recover and a panic occurs in a function on the same goroutine in which G -is executing. -When the running of deferred functions reaches D, -the return value of D's call to recover will be the value passed to the call of panic. -If D returns normally, without starting a new -panic, the panicking sequence stops. In that case, -the state of functions called between G and the call to panic -is discarded, and normal execution resumes. -Any functions deferred by G before D are then run and G's -execution terminates by returning to its caller. -

- -

-The return value of recover is nil if any of the following conditions holds: -

- - -

-The protect function in the example below invokes -the function argument g and protects callers from -run-time panics raised by g. -

- -
-func protect(g func()) {
-	defer func() {
-		log.Println("done")  // Println executes normally even if there is a panic
-		if x := recover(); x != nil {
-			log.Printf("run time panic: %v", x)
-		}
-	}()
-	log.Println("start")
-	g()
-}
-
- - -

Bootstrapping

- -

-Current implementations provide several built-in functions useful during -bootstrapping. These functions are documented for completeness but are not -guaranteed to stay in the language. They do not return a result. -

- -
-Function   Behavior
-
-print      prints all arguments; formatting of arguments is implementation-specific
-println    like print but prints spaces between arguments and a newline at the end
-
- -

-Implementation restriction: print and println need not -accept arbitrary argument types, but printing of boolean, numeric, and string -types must be supported. -

- -

Packages

- -

-Go programs are constructed by linking together packages. -A package in turn is constructed from one or more source files -that together declare constants, types, variables and functions -belonging to the package and which are accessible in all files -of the same package. Those elements may be -exported and used in another package. -

- -

Source file organization

- -

-Each source file consists of a package clause defining the package -to which it belongs, followed by a possibly empty set of import -declarations that declare packages whose contents it wishes to use, -followed by a possibly empty set of declarations of functions, -types, variables, and constants. -

- -
-SourceFile       = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
-
- -

Package clause

- -

-A package clause begins each source file and defines the package -to which the file belongs. -

- -
-PackageClause  = "package" PackageName .
-PackageName    = identifier .
-
- -

-The PackageName must not be the blank identifier. -

- -
-package math
-
- -

-A set of files sharing the same PackageName form the implementation of a package. -An implementation may require that all source files for a package inhabit the same directory. -

- -

Import declarations

- -

-An import declaration states that the source file containing the declaration -depends on functionality of the imported package -(§Program initialization and execution) -and enables access to exported identifiers -of that package. -The import names an identifier (PackageName) to be used for access and an ImportPath -that specifies the package to be imported. -

- -
-ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
-ImportSpec       = [ "." | PackageName ] ImportPath .
-ImportPath       = string_lit .
-
- -

-The PackageName is used in qualified identifiers -to access exported identifiers of the package within the importing source file. -It is declared in the file block. -If the PackageName is omitted, it defaults to the identifier specified in the -package clause of the imported package. -If an explicit period (.) appears instead of a name, all the -package's exported identifiers declared in that package's -package block will be declared in the importing source -file's file block and must be accessed without a qualifier. -

- -

-The interpretation of the ImportPath is implementation-dependent but -it is typically a substring of the full file name of the compiled -package and may be relative to a repository of installed packages. -

- -

-Implementation restriction: A compiler may restrict ImportPaths to -non-empty strings using only characters belonging to -Unicode's -L, M, N, P, and S general categories (the Graphic characters without -spaces) and may also exclude the characters -!"#$%&'()*,:;<=>?[\]^`{|} -and the Unicode replacement character U+FFFD. -

- -

-Assume we have compiled a package containing the package clause -package math, which exports function Sin, and -installed the compiled package in the file identified by -"lib/math". -This table illustrates how Sin is accessed in files -that import the package after the -various types of import declaration. -

- -
-Import declaration          Local name of Sin
-
-import   "lib/math"         math.Sin
-import m "lib/math"         m.Sin
-import . "lib/math"         Sin
-
- -

-An import declaration declares a dependency relation between -the importing and imported package. -It is illegal for a package to import itself, directly or indirectly, -or to directly import a package without -referring to any of its exported identifiers. To import a package solely for -its side-effects (initialization), use the blank -identifier as explicit package name: -

- -
-import _ "lib/math"
-
- - -

An example package

- -

-Here is a complete Go package that implements a concurrent prime sieve. -

- -
-package main
-
-import "fmt"
-
-// Send the sequence 2, 3, 4, … to channel 'ch'.
-func generate(ch chan<- int) {
-	for i := 2; ; i++ {
-		ch <- i  // Send 'i' to channel 'ch'.
-	}
-}
-
-// Copy the values from channel 'src' to channel 'dst',
-// removing those divisible by 'prime'.
-func filter(src <-chan int, dst chan<- int, prime int) {
-	for i := range src {  // Loop over values received from 'src'.
-		if i%prime != 0 {
-			dst <- i  // Send 'i' to channel 'dst'.
-		}
-	}
-}
-
-// The prime sieve: Daisy-chain filter processes together.
-func sieve() {
-	ch := make(chan int)  // Create a new channel.
-	go generate(ch)       // Start generate() as a subprocess.
-	for {
-		prime := <-ch
-		fmt.Print(prime, "\n")
-		ch1 := make(chan int)
-		go filter(ch, ch1, prime)
-		ch = ch1
-	}
-}
-
-func main() {
-	sieve()
-}
-
- -

Program initialization and execution

- -

The zero value

-

-When storage is allocated for a variable, -either through a declaration or a call of new, or when -a new value is created, either through a composite literal or a call -of make, -and no explicit initialization is provided, the variable or value is -given a default value. Each element of such a variable or value is -set to the zero value for its type: false for booleans, -0 for numeric types, "" -for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. -This initialization is done recursively, so for instance each element of an -array of structs will have its fields zeroed if no value is specified. -

-

-These two simple declarations are equivalent: -

- -
-var i int
-var i int = 0
-
- -

-After -

- -
-type T struct { i int; f float64; next *T }
-t := new(T)
-
- -

-the following holds: -

- -
-t.i == 0
-t.f == 0.0
-t.next == nil
-
- -

-The same would also be true after -

- -
-var t T
-
- -

Package initialization

- -

-Within a package, package-level variable initialization proceeds stepwise, -with each step selecting the variable earliest in declaration order -which has no dependencies on uninitialized variables. -

- -

-More precisely, a package-level variable is considered ready for -initialization if it is not yet initialized and either has -no initialization expression or -its initialization expression has no dependencies on uninitialized variables. -Initialization proceeds by repeatedly initializing the next package-level -variable that is earliest in declaration order and ready for initialization, -until there are no variables ready for initialization. -

- -

-If any variables are still uninitialized when this -process ends, those variables are part of one or more initialization cycles, -and the program is not valid. -

- -

-Multiple variables on the left-hand side of a variable declaration initialized -by single (multi-valued) expression on the right-hand side are initialized -together: If any of the variables on the left-hand side is initialized, all -those variables are initialized in the same step. -

- -
-var x = a
-var a, b = f() // a and b are initialized together, before x is initialized
-
- -

-For the purpose of package initialization, blank -variables are treated like any other variables in declarations. -

- -

-The declaration order of variables declared in multiple files is determined -by the order in which the files are presented to the compiler: Variables -declared in the first file are declared before any of the variables declared -in the second file, and so on. -

- -

-Dependency analysis does not rely on the actual values of the -variables, only on lexical references to them in the source, -analyzed transitively. For instance, if a variable x's -initialization expression refers to a function whose body refers to -variable y then x depends on y. -Specifically: -

- - - -

-For example, given the declarations -

- -
-var (
-	a = c + b  // == 9
-	b = f()    // == 4
-	c = f()    // == 5
-	d = 3      // == 5 after initialization has finished
-)
-
-func f() int {
-	d++
-	return d
-}
-
- -

-the initialization order is d, b, c, a. -Note that the order of subexpressions in initialization expressions is irrelevant: -a = c + b and a = b + c result in the same initialization -order in this example. -

- -

-Dependency analysis is performed per package; only references referring -to variables, functions, and (non-interface) methods declared in the current -package are considered. If other, hidden, data dependencies exists between -variables, the initialization order between those variables is unspecified. -

- -

-For instance, given the declarations -

- -
-var x = I(T{}).ab()   // x has an undetected, hidden dependency on a and b
-var _ = sideEffect()  // unrelated to x, a, or b
-var a = b
-var b = 42
-
-type I interface      { ab() []int }
-type T struct{}
-func (T) ab() []int   { return []int{a, b} }
-
- -

-the variable a will be initialized after b but -whether x is initialized before b, between -b and a, or after a, and -thus also the moment at which sideEffect() is called (before -or after x is initialized) is not specified. -

- -

-Variables may also be initialized using functions named init -declared in the package block, with no arguments and no result parameters. -

- -
-func init() { … }
-
- -

-Multiple such functions may be defined per package, even within a single -source file. In the package block, the init identifier can -be used only to declare init functions, yet the identifier -itself is not declared. Thus -init functions cannot be referred to from anywhere -in a program. -

- -

-A package with no imports is initialized by assigning initial values -to all its package-level variables followed by calling all init -functions in the order they appear in the source, possibly in multiple files, -as presented to the compiler. -If a package has imports, the imported packages are initialized -before initializing the package itself. If multiple packages import -a package, the imported package will be initialized only once. -The importing of packages, by construction, guarantees that there -can be no cyclic initialization dependencies. -

- -

-Package initialization—variable initialization and the invocation of -init functions—happens in a single goroutine, -sequentially, one package at a time. -An init function may launch other goroutines, which can run -concurrently with the initialization code. However, initialization -always sequences -the init functions: it will not invoke the next one -until the previous one has returned. -

- -

-To ensure reproducible initialization behavior, build systems are encouraged -to present multiple files belonging to the same package in lexical file name -order to a compiler. -

- - -

Program execution

-

-A complete program is created by linking a single, unimported package -called the main package with all the packages it imports, transitively. -The main package must -have package name main and -declare a function main that takes no -arguments and returns no value. -

- -
-func main() { … }
-
- -

-Program execution begins by initializing the main package and then -invoking the function main. -When that function invocation returns, the program exits. -It does not wait for other (non-main) goroutines to complete. -

- -

Errors

- -

-The predeclared type error is defined as -

- -
-type error interface {
-	Error() string
-}
-
- -

-It is the conventional interface for representing an error condition, -with the nil value representing no error. -For instance, a function to read data from a file might be defined: -

- -
-func Read(f *File, b []byte) (n int, err error)
-
- -

Run-time panics

- -

-Execution errors such as attempting to index an array out -of bounds trigger a run-time panic equivalent to a call of -the built-in function panic -with a value of the implementation-defined interface type runtime.Error. -That type satisfies the predeclared interface type -error. -The exact error values that -represent distinct run-time error conditions are unspecified. -

- -
-package runtime
-
-type Error interface {
-	error
-	// and perhaps other methods
-}
-
- -

System considerations

- -

Package unsafe

- -

-The built-in package unsafe, known to the compiler -and accessible through the import path "unsafe", -provides facilities for low-level programming including operations -that violate the type system. A package using unsafe -must be vetted manually for type safety and may not be portable. -The package provides the following interface: -

- -
-package unsafe
-
-type ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type
-type Pointer *ArbitraryType
-
-func Alignof(variable ArbitraryType) uintptr
-func Offsetof(selector ArbitraryType) uintptr
-func Sizeof(variable ArbitraryType) uintptr
-
-type IntegerType int  // shorthand for an integer type; it is not a real type
-func Add(ptr Pointer, len IntegerType) Pointer
-func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
-
- -

-A Pointer is a pointer type but a Pointer -value may not be dereferenced. -Any pointer or value of underlying type uintptr can be converted to -a type of underlying type Pointer and vice versa. -The effect of converting between Pointer and uintptr is implementation-defined. -

- -
-var f float64
-bits = *(*uint64)(unsafe.Pointer(&f))
-
-type ptr unsafe.Pointer
-bits = *(*uint64)(ptr(&f))
-
-var p ptr = nil
-
- -

-The functions Alignof and Sizeof take an expression x -of any type and return the alignment or size, respectively, of a hypothetical variable v -as if v was declared via var v = x. -

-

-The function Offsetof takes a (possibly parenthesized) selector -s.f, denoting a field f of the struct denoted by s -or *s, and returns the field offset in bytes relative to the struct's address. -If f is an embedded field, it must be reachable -without pointer indirections through fields of the struct. -For a struct s with field f: -

- -
-uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))
-
- -

-Computer architectures may require memory addresses to be aligned; -that is, for addresses of a variable to be a multiple of a factor, -the variable's type's alignment. The function Alignof -takes an expression denoting a variable of any type and returns the -alignment of the (type of the) variable in bytes. For a variable -x: -

- -
-uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0
-
- -

-Calls to Alignof, Offsetof, and -Sizeof are compile-time constant expressions of type uintptr. -

- -

-The function Add adds len to ptr -and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)). -The len argument must be of integer type or an untyped constant. -A constant len argument must be representable by a value of type int; -if it is an untyped constant it is given type int. -The rules for valid uses of Pointer still apply. -

- -

-The function Slice returns a slice whose underlying array starts at ptr -and whose length and capacity are len. -Slice(ptr, len) is equivalent to -

- -
-(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]
-
- -

-except that, as a special case, if ptr -is nil and len is zero, -Slice returns nil. -

- -

-The len argument must be of integer type or an untyped constant. -A constant len argument must be non-negative and representable by a value of type int; -if it is an untyped constant it is given type int. -At run time, if len is negative, -or if ptr is nil and len is not zero, -a run-time panic occurs. -

- -

Size and alignment guarantees

- -

-For the numeric types, the following sizes are guaranteed: -

- -
-type                                 size in bytes
-
-byte, uint8, int8                     1
-uint16, int16                         2
-uint32, int32, float32                4
-uint64, int64, float64, complex64     8
-complex128                           16
-
- -

-The following minimal alignment properties are guaranteed: -

-
    -
  1. For a variable x of any type: unsafe.Alignof(x) is at least 1. -
  2. - -
  3. For a variable x of struct type: unsafe.Alignof(x) is the largest of - all the values unsafe.Alignof(x.f) for each field f of x, but at least 1. -
  4. - -
  5. For a variable x of array type: unsafe.Alignof(x) is the same as - the alignment of a variable of the array's element type. -
  6. -
- -

-A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory. -

diff --git a/doc/go_spec.html b/doc/go_spec.html index 31bea3713a7c82..9865227c22d454 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -8,8 +8,6 @@

Introduction

This is the reference manual for the Go programming language. -The pre-Go1.18 version, without generics, can be found -here. For more information and other documents, see go.dev.

@@ -798,7 +796,6 @@

Variables

zero value for its type.

-

Types

@@ -810,12 +807,12 @@

Types

-Type      = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
-TypeName  = identifier | QualifiedIdent .
-TypeArgs  = "[" TypeList [ "," ] "]" .
-TypeList  = Type { "," Type } .
-TypeLit   = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
-            SliceType | MapType | ChannelType .
+Type     = TypeName [ TypeArgs ] | TypeLit | "(" Type ")" .
+TypeName = identifier | QualifiedIdent .
+TypeArgs = "[" TypeList [ "," ] "]" .
+TypeList = Type { "," Type } .
+TypeLit  = ArrayType | StructType | PointerType | FunctionType | InterfaceType |
+           SliceType | MapType | ChannelType .
 

@@ -1200,7 +1197,7 @@

Pointer types

A pointer type denotes the set of all pointers to variables of a given type, called the base type of the pointer. -The value of an uninitialized pointer is nil. +The value of an uninitialized pointer is nil.

@@ -1216,18 +1213,18 @@ 

Pointer types

Function types

-A function type denotes the set of all functions with the same parameter -and result types. The value of an uninitialized variable of function type -is nil. +A function type denotes the set of all functions with the same parameter and result types. +The value of an uninitialized variable of function +type is nil.

-FunctionType   = "func" Signature .
-Signature      = Parameters [ Result ] .
-Result         = Parameters | Type .
-Parameters     = "(" [ ParameterList [ "," ] ] ")" .
-ParameterList  = ParameterDecl { "," ParameterDecl } .
-ParameterDecl  = [ IdentifierList ] [ "..." ] Type .
+FunctionType  = "func" Signature .
+Signature     = Parameters [ Result ] .
+Result        = Parameters | Type .
+Parameters    = "(" [ ParameterList [ "," ] ] ")" .
+ParameterList = ParameterDecl { "," ParameterDecl } .
+ParameterDecl = [ IdentifierList ] [ "..." ] Type .
 

@@ -1267,7 +1264,8 @@

Interface types

A variable of interface type can store a value of any type that is in the type set of the interface. Such a type is said to implement the interface. -The value of an uninitialized variable of interface type is nil. +The value of an uninitialized variable of +interface type is nil.

@@ -1630,12 +1628,12 @@ 

Map types

A map is an unordered group of elements of one type, called the element type, indexed by a set of unique keys of another type, called the key type. -The value of an uninitialized map is nil. +The value of an uninitialized map is nil.

-MapType     = "map" "[" KeyType "]" ElementType .
-KeyType     = Type .
+MapType = "map" "[" KeyType "]" ElementType .
+KeyType = Type .
 

@@ -1693,7 +1691,7 @@

Channel types

sending and receiving values of a specified element type. -The value of an uninitialized channel is nil. +The value of an uninitialized channel is nil.

@@ -1772,6 +1770,57 @@ 

Channel types

Properties of types and values

+

Representation of values

+ +

+Values of predeclared types (see below for the interfaces any +and error), arrays, and structs are self-contained: +Each such value contains a complete copy of all its data, +and variables of such types store the entire value. +For instance, an array variable provides the storage (the variables) +for all elements of the array. +The respective zero values are specific to the +value's types; they are never nil. +

+ +

+Non-nil pointer, function, slice, map, and channel values contain references +to underlying data which may be shared by multiple values: +

+ +
    +
  • + A pointer value is a reference to the variable holding + the pointer base type value. +
  • +
  • + A function value contains references to the (possibly + anonymous) function + and enclosed variables. +
  • +
  • + A slice value contains the slice length, capacity, and + a reference to its underlying array. +
  • +
  • + A map or channel value is a reference to the implementation-specific + data structure of the map or channel. +
  • +
+ +

+An interface value may be self-contained or contain references to underlying data +depending on the interface's dynamic type. +The predeclared identifier nil is the zero value for types whose values +can contain references. +

+ +

+When multiple values share underlying data, changing one value may change another. +For instance, changing an element of a slice will change +that element in the underlying array for all slices that share the array. +

+

Underlying types

@@ -2176,7 +2225,7 @@

Blocks

-Block = "{" StatementList "}" .
+Block         = "{" StatementList "}" .
 StatementList = { Statement ";" } .
 
@@ -2233,8 +2282,8 @@

Declarations and scope

-Declaration   = ConstDecl | TypeDecl | VarDecl .
-TopLevelDecl  = Declaration | FunctionDecl | MethodDecl .
+Declaration  = ConstDecl | TypeDecl | VarDecl .
+TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
 

@@ -2679,9 +2728,9 @@

Type parameter declarations

-TypeParameters  = "[" TypeParamList [ "," ] "]" .
-TypeParamList   = TypeParamDecl { "," TypeParamDecl } .
-TypeParamDecl   = IdentifierList TypeConstraint .
+TypeParameters = "[" TypeParamList [ "," ] "]" .
+TypeParamList  = TypeParamDecl { "," TypeParamDecl } .
+TypeParamDecl  = IdentifierList TypeConstraint .
 

@@ -2819,7 +2868,7 @@

Satisfying a type constraint

A type argument T satisfies a type constraint C -if T is an element of the type set defined by C; i.e., +if T is an element of the type set defined by C; in other words, if T implements C. As an exception, a strictly comparable type constraint may also be satisfied by a comparable @@ -2869,8 +2918,8 @@

Variable declarations

-VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
-VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
+VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
+VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
 
@@ -2899,7 +2948,7 @@ 

Variable declarations

If that value is an untyped constant, it is first implicitly converted to its default type; if it is an untyped boolean value, it is first implicitly converted to type bool. -The predeclared value nil cannot be used to initialize a variable +The predeclared identifier nil cannot be used to initialize a variable with no explicit type.

@@ -3210,15 +3259,15 @@

Composite literals

-CompositeLit  = LiteralType LiteralValue .
-LiteralType   = StructType | ArrayType | "[" "..." "]" ElementType |
-                SliceType | MapType | TypeName [ TypeArgs ] .
-LiteralValue  = "{" [ ElementList [ "," ] ] "}" .
-ElementList   = KeyedElement { "," KeyedElement } .
-KeyedElement  = [ Key ":" ] Element .
-Key           = FieldName | Expression | LiteralValue .
-FieldName     = identifier .
-Element       = Expression | LiteralValue .
+CompositeLit = LiteralType LiteralValue .
+LiteralType  = StructType | ArrayType | "[" "..." "]" ElementType |
+               SliceType | MapType | TypeName [ TypeArgs ] .
+LiteralValue = "{" [ ElementList [ "," ] ] "}" .
+ElementList  = KeyedElement { "," KeyedElement } .
+KeyedElement = [ Key ":" ] Element .
+Key          = FieldName | Expression | LiteralValue .
+FieldName    = identifier .
+Element      = Expression | LiteralValue .
 

@@ -3450,22 +3499,21 @@

Primary expressions

-PrimaryExpr =
-	Operand |
-	Conversion |
-	MethodExpr |
-	PrimaryExpr Selector |
-	PrimaryExpr Index |
-	PrimaryExpr Slice |
-	PrimaryExpr TypeAssertion |
-	PrimaryExpr Arguments .
+PrimaryExpr   = Operand |
+                Conversion |
+                MethodExpr |
+                PrimaryExpr Selector |
+                PrimaryExpr Index |
+                PrimaryExpr Slice |
+                PrimaryExpr TypeAssertion |
+                PrimaryExpr Arguments .
 
-Selector       = "." identifier .
-Index          = "[" Expression [ "," ] "]" .
-Slice          = "[" [ Expression ] ":" [ Expression ] "]" |
-                 "[" [ Expression ] ":" Expression ":" Expression "]" .
-TypeAssertion  = "." "(" Type ")" .
-Arguments      = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
+Selector      = "." identifier .
+Index         = "[" Expression [ "," ] "]" .
+Slice         = "[" [ Expression ] ":" [ Expression ] "]" |
+                "[" [ Expression ] ":" Expression ":" Expression "]" .
+TypeAssertion = "." "(" Type ")" .
+Arguments     = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
 
@@ -3638,8 +3686,8 @@

Method expressions

-MethodExpr    = ReceiverType "." MethodName .
-ReceiverType  = Type .
+MethodExpr   = ReceiverType "." MethodName .
+ReceiverType = Type .
 

@@ -4230,8 +4278,7 @@

Calls

Except for one special case, arguments must be single-valued expressions assignable to the parameter types of F and are evaluated before the function is called. -The type of the expression is the result type -of F. +The type of the expression is the result type of F. A method invocation is similar but the method itself is specified as a selector upon a value of the receiver type for the method. @@ -4252,9 +4299,14 @@

Calls

In a function call, the function value and arguments are evaluated in the usual order. -After they are evaluated, the parameters of the call are passed by value to the function +After they are evaluated, new storage is allocated for the function's +variables, which includes its parameters +and results. +Then, the arguments of the call are passed to the function, +which means that they are assigned +to their corresponding function parameters, and the called function begins execution. -The return parameters of the function are passed by value +The return parameters of the function are passed back to the caller when the function returns.

@@ -4268,9 +4320,9 @@

Calls

g are equal in number and individually assignable to the parameters of another function or method f, then the call f(g(parameters_of_g)) -will invoke f after binding the return values of -g to the parameters of f in order. The call -of f must contain no parameters other than the call of g, +will invoke f after passing the return values of +g to the parameters of f in order. +The call of f must contain no parameters other than the call of g, and g must have at least one return value. If f has a final ... parameter, it is assigned the return values of g that remain after @@ -4316,7 +4368,7 @@

Passing arguments to ...p of type ...T, then within f the type of p is equivalent to type []T. If f is invoked with no actual arguments for p, -the value passed to p is nil. +the value passed to p is nil. Otherwise, the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable @@ -5632,6 +5684,8 @@

Conversions to and from a string
  • Converting a value of a string type to a slice of bytes type yields a non-nil slice whose successive elements are the bytes of the string. +The capacity of the resulting slice is +implementation-specific and may be larger than the slice length.
     []byte("hellø")             // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
    @@ -5647,6 +5701,8 @@ 

    Conversions to and from a string
  • Converting a value of a string type to a slice of runes type yields a slice containing the individual Unicode code points of the string. +The capacity of the resulting slice is +implementation-specific and may be larger than the slice length.
     []rune(myString("白鵬翔"))   // []rune{0x767d, 0x9d6c, 0x7fd4}
    @@ -5848,7 +5904,7 @@ 

    Order of evaluation

    expression, assignment, or return statement, all function calls, method calls, -receive operations, +receive operations, and binary logical operations are evaluated in lexical left-to-right order.

    @@ -5916,11 +5972,10 @@

    Statements

    -Statement =
    -	Declaration | LabeledStmt | SimpleStmt |
    -	GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
    -	FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
    -	DeferStmt .
    +Statement  = Declaration | LabeledStmt | SimpleStmt |
    +             GoStmt | ReturnStmt | BreakStmt | ContinueStmt | GotoStmt |
    +             FallthroughStmt | Block | IfStmt | SwitchStmt | SelectStmt | ForStmt |
    +             DeferStmt .
     
     SimpleStmt = EmptyStmt | ExpressionStmt | SendStmt | IncDecStmt | Assignment | ShortVarDecl .
     
    @@ -6132,7 +6187,7 @@

    Assignment statements

     Assignment = ExpressionList assign_op ExpressionList .
     
    -assign_op = [ add_op | mul_op ] "=" .
    +assign_op  = [ add_op | mul_op ] "=" .
     

    @@ -6261,6 +6316,26 @@

    Assignment statements

  • +

    +When a value is assigned to a variable, only the data that is stored in the variable +is replaced. If the value contains a reference, +the assignment copies the reference but does not make a copy of the referenced data +(such as the underlying array of a slice). +

    + +
    +var s1 = []int{1, 2, 3}
    +var s2 = s1                    // s2 stores the slice descriptor of s1
    +s1 = s1[:1]                    // s1's length is 1 but it still shares its underlying array with s2
    +s2[0] = 42                     // setting s2[0] changes s1[0] as well
    +fmt.Println(s1, s2)            // prints [42] [42 2 3]
    +
    +var m1 = make(map[string]int)
    +var m2 = m1                    // m2 stores the map descriptor of m1
    +m1["foo"] = 42                 // setting m1["foo"] changes m2["foo"] as well
    +fmt.Println(m2["foo"])         // prints 42
    +
    +

    If statements

    @@ -6548,7 +6623,7 @@

    For statements

    -ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .
    +ForStmt   = "for" [ Condition | ForClause | RangeClause ] Block .
     Condition = Expression .
     
    @@ -6580,8 +6655,8 @@

    For statements with for clause

     ForClause = [ InitStmt ] ";" [ Condition ] ";" [ PostStmt ] .
    -InitStmt = SimpleStmt .
    -PostStmt = SimpleStmt .
    +InitStmt  = SimpleStmt .
    +PostStmt  = SimpleStmt .
     
    @@ -7909,7 +7984,7 @@ 

    Source file organization

    -SourceFile       = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
    +SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .
     

    Package clause

    @@ -7920,8 +7995,8 @@

    Package clause

    -PackageClause  = "package" PackageName .
    -PackageName    = identifier .
    +PackageClause = "package" PackageName .
    +PackageName   = identifier .
     

    @@ -7950,9 +8025,9 @@

    Import declarations

    -ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
    -ImportSpec       = [ "." | PackageName ] ImportPath .
    -ImportPath       = string_lit .
    +ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
    +ImportSpec = [ "." | PackageName ] ImportPath .
    +ImportPath = string_lit .
     

    @@ -8437,7 +8512,7 @@

    Package unsafe

    The functions Alignof and Sizeof take an expression x of any type and return the alignment or size, respectively, of a hypothetical variable v -as if v was declared via var v = x. +as if v were declared via var v = x.

    The function Offsetof takes a (possibly parenthesized) selector diff --git a/doc/godebug.md b/doc/godebug.md index 1b5674f2cd0b5b..cdc09ddcc4792d 100644 --- a/doc/godebug.md +++ b/doc/godebug.md @@ -153,6 +153,17 @@ and the [go command documentation](/cmd/go#hdr-Build_and_test_caching). ### Go 1.24 +Go 1.24 added a new `fips140` setting that controls whether the Go +Cryptographic Module operates in FIPS 140-3 mode. +The possible values are: +- "off": no special support for FIPS 140-3 mode. This is the default. +- "on": the Go Cryptographic Module operates in FIPS 140-3 mode. +- "only": like "on", but cryptographic algorithms not approved by + FIPS 140-3 return an error or panic. +For more information, see [FIPS 140-3 Compliance](/doc/security/fips140). +This setting is fixed at program startup time, and can't be modified +by changing the `GODEBUG` environment variable after the program starts. + Go 1.24 changed the global [`math/rand.Seed`](/pkg/math/rand/#Seed) to be a no-op. This behavior is controlled by the `randseednop` setting. For Go 1.24 it defaults to `randseednop=1`. diff --git a/doc/initial/1-intro.md b/doc/initial/1-intro.md index 8c9948ddf607ae..84ffee855a4095 100644 --- a/doc/initial/1-intro.md +++ b/doc/initial/1-intro.md @@ -1,9 +1,3 @@ - - diff --git a/doc/next/1-intro.md b/doc/next/1-intro.md new file mode 100644 index 00000000000000..77a9aed59f244c --- /dev/null +++ b/doc/next/1-intro.md @@ -0,0 +1,8 @@ + + +## DRAFT RELEASE NOTES — Introduction to Go 1.N {#introduction} + +**Go 1.25 is not yet released. These are work-in-progress release notes. +Go 1.25 is expected to be released in August 2025.** diff --git a/doc/next/2-language.md b/doc/next/2-language.md new file mode 100644 index 00000000000000..61030bd67606b0 --- /dev/null +++ b/doc/next/2-language.md @@ -0,0 +1,3 @@ +## Changes to the language {#language} + + diff --git a/doc/next/3-tools.md b/doc/next/3-tools.md new file mode 100644 index 00000000000000..5638f240a5b127 --- /dev/null +++ b/doc/next/3-tools.md @@ -0,0 +1,6 @@ +## Tools {#tools} + +### Go command {#go-command} + +### Cgo {#cgo} + diff --git a/doc/next/4-runtime.md b/doc/next/4-runtime.md new file mode 100644 index 00000000000000..28483eb519d342 --- /dev/null +++ b/doc/next/4-runtime.md @@ -0,0 +1,18 @@ +## Runtime {#runtime} + + + +The message printed when a program exits due to an unhandled panic +that was recovered and re-raised no longer repeats the text of +the panic value. + +Previously, a program which panicked with `panic("PANIC")`, +recovered the panic, and then re-panicked with the original +value would print: + + panic: PANIC [recovered] + panic: PANIC + +This program will now print: + + panic: PANIC [recovered, reraised] diff --git a/doc/next/5-toolchain.md b/doc/next/5-toolchain.md new file mode 100644 index 00000000000000..0f4a816479754c --- /dev/null +++ b/doc/next/5-toolchain.md @@ -0,0 +1,7 @@ +## Compiler {#compiler} + +## Assembler {#assembler} + +## Linker {#linker} + + diff --git a/doc/next/6-stdlib/0-heading.md b/doc/next/6-stdlib/0-heading.md new file mode 100644 index 00000000000000..a992170d433326 --- /dev/null +++ b/doc/next/6-stdlib/0-heading.md @@ -0,0 +1,2 @@ +## Standard library {#library} + diff --git a/doc/next/6-stdlib/99-minor/0-heading.md b/doc/next/6-stdlib/99-minor/0-heading.md new file mode 100644 index 00000000000000..a98105e8ccba7f --- /dev/null +++ b/doc/next/6-stdlib/99-minor/0-heading.md @@ -0,0 +1,3 @@ +### Minor changes to the library {#minor_library_changes} + + diff --git a/doc/next/6-stdlib/99-minor/README b/doc/next/6-stdlib/99-minor/README new file mode 100644 index 00000000000000..fac778de050642 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/README @@ -0,0 +1 @@ +API changes and other small changes to the standard library go here. diff --git a/doc/next/6-stdlib/99-minor/archive/tar/49580.md b/doc/next/6-stdlib/99-minor/archive/tar/49580.md new file mode 100644 index 00000000000000..8fa43681fa4163 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/archive/tar/49580.md @@ -0,0 +1,2 @@ +The [*Writer.AddFS] implementation now supports symbolic links +for filesystems that implement [io/fs.ReadLinkFS]. diff --git a/doc/next/6-stdlib/99-minor/io/fs/49580.md b/doc/next/6-stdlib/99-minor/io/fs/49580.md new file mode 100644 index 00000000000000..c1cba5a3957dcb --- /dev/null +++ b/doc/next/6-stdlib/99-minor/io/fs/49580.md @@ -0,0 +1 @@ +A new [ReadLinkFS] interface provides the ability to read symbolic links in a filesystem. diff --git a/doc/next/6-stdlib/99-minor/os/49580.md b/doc/next/6-stdlib/99-minor/os/49580.md new file mode 100644 index 00000000000000..18d8831e7be6ef --- /dev/null +++ b/doc/next/6-stdlib/99-minor/os/49580.md @@ -0,0 +1,2 @@ +The filesystem returned by [DirFS] implements the new [io/fs.ReadLinkFS] interface. +[CopyFS] supports symlinks when copying filesystems that implement [io/fs.ReadLinkFS]. diff --git a/doc/next/6-stdlib/99-minor/os/67002.md b/doc/next/6-stdlib/99-minor/os/67002.md new file mode 100644 index 00000000000000..04ff6d5de0fec0 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/os/67002.md @@ -0,0 +1,4 @@ +The [os.Root] type supports the following additional methods: + + * [os.Root.Chmod] + * [os.Root.Chown] diff --git a/doc/next/6-stdlib/99-minor/testing/fstest/49580.md b/doc/next/6-stdlib/99-minor/testing/fstest/49580.md new file mode 100644 index 00000000000000..5b3c0d6a84eb22 --- /dev/null +++ b/doc/next/6-stdlib/99-minor/testing/fstest/49580.md @@ -0,0 +1,3 @@ +[MapFS] implements the new [io/fs.ReadLinkFS] interface. +[TestFS] will verify the functionality of the [io/fs.ReadLinkFS] interface if implemented. +[TestFS] will no longer follow symlinks to avoid unbounded recursion. diff --git a/doc/next/7-ports.md b/doc/next/7-ports.md new file mode 100644 index 00000000000000..8bea3f8fbc33f9 --- /dev/null +++ b/doc/next/7-ports.md @@ -0,0 +1,2 @@ +## Ports {#ports} + diff --git a/lib/fips140/Makefile b/lib/fips140/Makefile index cd657ae72fd248..8dcb8fbebe5bec 100644 --- a/lib/fips140/Makefile +++ b/lib/fips140/Makefile @@ -27,7 +27,7 @@ default: # copy and edit the 'go run' command by hand to use a different branch. v%.zip: git fetch origin master - go run ../../src/cmd/go/internal/fips140/mkzip.go -b master v$* + go run ../../src/cmd/go/internal/fips140/mkzip.go v$* # normally mkzip refuses to overwrite an existing zip file. # make v1.2.3.rm removes the zip file and and unpacked diff --git a/lib/fips140/fips140.sum b/lib/fips140/fips140.sum index 013112d9e58d0d..66b1e23dfe619d 100644 --- a/lib/fips140/fips140.sum +++ b/lib/fips140/fips140.sum @@ -9,3 +9,4 @@ # # go test cmd/go/internal/fips140 -update # +v1.0.0.zip b50508feaeff05d22516b21e1fd210bbf5d6a1e422eaf2cfa23fe379342713b8 diff --git a/lib/fips140/v1.0.0.zip b/lib/fips140/v1.0.0.zip new file mode 100644 index 00000000000000..bd9d3c19d05b90 Binary files /dev/null and b/lib/fips140/v1.0.0.zip differ diff --git a/lib/time/update.bash b/lib/time/update.bash index 6b66fa54a9595a..940596fb11221f 100755 --- a/lib/time/update.bash +++ b/lib/time/update.bash @@ -24,8 +24,8 @@ # in the CL match the update.bash in the CL. # Versions to use. -CODE=2024b -DATA=2024b +CODE=2025a +DATA=2025a set -e @@ -40,7 +40,12 @@ curl -sS -L -O https://www.iana.org/time-zones/repository/releases/tzdata$DATA.t tar xzf tzcode$CODE.tar.gz tar xzf tzdata$DATA.tar.gz -if ! make CFLAGS=-DSTD_INSPIRED AWK=awk TZDIR=zoneinfo posix_only >make.out 2>&1; then +# The PACKRATLIST and PACKRATDATA options are copied from Ubuntu: +# https://git.launchpad.net/ubuntu/+source/tzdata/tree/debian/rules?h=debian/sid +# +# You can see the description of these make variables in the tzdata Makefile: +# https://github.com/eggert/tz/blob/main/Makefile +if ! make CFLAGS=-DSTD_INSPIRED AWK=awk TZDIR=zoneinfo PACKRATDATA=backzone PACKRATLIST=zone.tab posix_only >make.out 2>&1; then cat make.out exit 2 fi diff --git a/lib/time/zoneinfo.zip b/lib/time/zoneinfo.zip index b36e82c958aef0..f8099b1b494fc9 100644 Binary files a/lib/time/zoneinfo.zip and b/lib/time/zoneinfo.zip differ diff --git a/src/all.bat b/src/all.bat index d5abec141f31aa..4c681d15eb643e 100644 --- a/src/all.bat +++ b/src/all.bat @@ -6,17 +6,11 @@ setlocal -if exist make.bat goto ok -echo all.bat must be run from go\src -:: cannot exit: would kill parent command interpreter -goto end -:ok +if not exist make.bat ( + echo all.bat must be run from go\src + exit /b 1 +) -call .\make.bat --no-banner --no-local -if %GOBUILDFAIL%==1 goto end -call .\run.bat --no-rebuild --no-local -if %GOBUILDFAIL%==1 goto end -"%GOTOOLDIR%/dist" banner - -:end -if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL% +call .\make.bat --no-banner || exit /b 1 +call .\run.bat --no-rebuild || exit /b 1 +..\bin\go tool dist banner diff --git a/src/archive/tar/writer.go b/src/archive/tar/writer.go index 059669767f2b9d..336c9fd758e733 100644 --- a/src/archive/tar/writer.go +++ b/src/archive/tar/writer.go @@ -415,19 +415,28 @@ func (tw *Writer) AddFS(fsys fs.FS) error { if err != nil { return err } - // TODO(#49580): Handle symlinks when fs.ReadLinkFS is available. - if !d.IsDir() && !info.Mode().IsRegular() { + linkTarget := "" + if typ := d.Type(); typ == fs.ModeSymlink { + var err error + linkTarget, err = fs.ReadLink(fsys, name) + if err != nil { + return err + } + } else if !typ.IsRegular() && typ != fs.ModeDir { return errors.New("tar: cannot add non-regular file") } - h, err := FileInfoHeader(info, "") + h, err := FileInfoHeader(info, linkTarget) if err != nil { return err } h.Name = name + if d.IsDir() { + h.Name += "/" + } if err := tw.WriteHeader(h); err != nil { return err } - if d.IsDir() { + if !d.Type().IsRegular() { return nil } f, err := fsys.Open(name) diff --git a/src/archive/tar/writer_test.go b/src/archive/tar/writer_test.go index 2a01915d368c5a..9e484432ea90b6 100644 --- a/src/archive/tar/writer_test.go +++ b/src/archive/tar/writer_test.go @@ -1342,6 +1342,7 @@ func TestWriterAddFS(t *testing.T) { "emptyfolder": {Mode: 0o755 | os.ModeDir}, "file.go": {Data: []byte("hello")}, "subfolder/another.go": {Data: []byte("world")}, + "symlink.go": {Mode: 0o777 | os.ModeSymlink, Data: []byte("file.go")}, // Notably missing here is the "subfolder" directory. This makes sure even // if we don't have a subfolder directory listed. } @@ -1370,7 +1371,7 @@ func TestWriterAddFS(t *testing.T) { for _, name := range names { entriesLeft-- - entryInfo, err := fsys.Stat(name) + entryInfo, err := fsys.Lstat(name) if err != nil { t.Fatalf("getting entry info error: %v", err) } @@ -1382,7 +1383,11 @@ func TestWriterAddFS(t *testing.T) { t.Fatal(err) } - if hdr.Name != name { + tmpName := name + if entryInfo.IsDir() { + tmpName += "/" + } + if hdr.Name != tmpName { t.Errorf("test fs has filename %v; archive header has %v", name, hdr.Name) } @@ -1392,18 +1397,23 @@ func TestWriterAddFS(t *testing.T) { name, entryInfo.Mode(), hdr.FileInfo().Mode()) } - if entryInfo.IsDir() { - continue - } - - data, err := io.ReadAll(tr) - if err != nil { - t.Fatal(err) - } - origdata := fsys[name].Data - if string(data) != string(origdata) { - t.Fatalf("test fs has file content %v; archive header has %v", - data, origdata) + switch entryInfo.Mode().Type() { + case fs.ModeDir: + // No additional checks necessary. + case fs.ModeSymlink: + origtarget := string(fsys[name].Data) + if hdr.Linkname != origtarget { + t.Fatalf("test fs has link content %s; archive header %v", origtarget, hdr.Linkname) + } + default: + data, err := io.ReadAll(tr) + if err != nil { + t.Fatal(err) + } + origdata := fsys[name].Data + if string(data) != string(origdata) { + t.Fatalf("test fs has file content %v; archive header has %v", origdata, data) + } } } if entriesLeft > 0 { diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go index cbe5ba262747f6..0a310054e37678 100644 --- a/src/archive/zip/writer.go +++ b/src/archive/zip/writer.go @@ -520,6 +520,9 @@ func (w *Writer) AddFS(fsys fs.FS) error { return err } h.Name = name + if d.IsDir() { + h.Name += "/" + } h.Method = Deflate fw, err := w.CreateHeader(h) if err != nil { diff --git a/src/archive/zip/writer_test.go b/src/archive/zip/writer_test.go index 27a99b6b3a13b9..44592ce8318826 100644 --- a/src/archive/zip/writer_test.go +++ b/src/archive/zip/writer_test.go @@ -633,7 +633,7 @@ func TestWriterAddFS(t *testing.T) { t.Fatal(err) } - // Add subfolder into fsys to match what we'll read from the tar. + // Add subfolder into fsys to match what we'll read from the zip. tests = append(tests[:2:2], WriteTest{Name: "subfolder", Mode: 0o555 | os.ModeDir}, tests[2]) // read it back @@ -642,6 +642,9 @@ func TestWriterAddFS(t *testing.T) { t.Fatal(err) } for i, wt := range tests { + if wt.Mode.IsDir() { + wt.Name += "/" + } testReadFile(t, r.File[i], &wt) } } diff --git a/src/bufio/bufio.go b/src/bufio/bufio.go index d589701e19a85c..5244ce2e0ca943 100644 --- a/src/bufio/bufio.go +++ b/src/bufio/bufio.go @@ -519,9 +519,11 @@ func (b *Reader) WriteTo(w io.Writer) (n int64, err error) { b.lastByte = -1 b.lastRuneSize = -1 - n, err = b.writeBuf(w) - if err != nil { - return + if b.r < b.w { + n, err = b.writeBuf(w) + if err != nil { + return + } } if r, ok := b.rd.(io.WriterTo); ok { diff --git a/src/bufio/bufio_test.go b/src/bufio/bufio_test.go index 63dd2ea4322cea..742e1954256903 100644 --- a/src/bufio/bufio_test.go +++ b/src/bufio/bufio_test.go @@ -1149,7 +1149,7 @@ func (w errorWriterToTest) Write(p []byte) (int, error) { var errorWriterToTests = []errorWriterToTest{ {1, 0, nil, io.ErrClosedPipe, io.ErrClosedPipe}, {0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe}, - {0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrClosedPipe}, + {0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrUnexpectedEOF}, {0, 1, io.EOF, nil, nil}, } diff --git a/src/bufio/net_test.go b/src/bufio/net_test.go new file mode 100644 index 00000000000000..d3b47e4cb99a39 --- /dev/null +++ b/src/bufio/net_test.go @@ -0,0 +1,96 @@ +// Copyright 2025 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. + +//go:build unix + +package bufio_test + +import ( + "bufio" + "io" + "net" + "path/filepath" + "strings" + "sync" + "testing" +) + +// TestCopyUnixpacket tests that we can use bufio when copying +// across a unixpacket socket. This used to fail due to an unnecessary +// empty Write call that was interpreted as an EOF. +func TestCopyUnixpacket(t *testing.T) { + tmpDir := t.TempDir() + socket := filepath.Join(tmpDir, "unixsock") + + // Start a unixpacket server. + addr := &net.UnixAddr{ + Name: socket, + Net: "unixpacket", + } + server, err := net.ListenUnix("unixpacket", addr) + if err != nil { + t.Skipf("skipping test because opening a unixpacket socket failed: %v", err) + } + + // Start a goroutine for the server to accept one connection + // and read all the data sent on the connection, + // reporting the number of bytes read on ch. + ch := make(chan int, 1) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + + tot := 0 + defer func() { + ch <- tot + }() + + serverConn, err := server.Accept() + if err != nil { + t.Error(err) + return + } + + buf := make([]byte, 1024) + for { + n, err := serverConn.Read(buf) + tot += n + if err == io.EOF { + return + } + if err != nil { + t.Error(err) + return + } + } + }() + + clientConn, err := net.DialUnix("unixpacket", nil, addr) + if err != nil { + // Leaves the server goroutine hanging. Oh well. + t.Fatal(err) + } + + defer wg.Wait() + defer clientConn.Close() + + const data = "data" + r := bufio.NewReader(strings.NewReader(data)) + n, err := io.Copy(clientConn, r) + if err != nil { + t.Fatal(err) + } + + if n != int64(len(data)) { + t.Errorf("io.Copy returned %d, want %d", n, len(data)) + } + + clientConn.Close() + tot := <-ch + + if tot != len(data) { + t.Errorf("server read %d, want %d", tot, len(data)) + } +} diff --git a/src/builtin/builtin.go b/src/builtin/builtin.go index 215c59c4ae8156..afa2a10f9091dc 100644 --- a/src/builtin/builtin.go +++ b/src/builtin/builtin.go @@ -162,12 +162,12 @@ func delete(m map[Type]Type1, key Type) // The len built-in function returns the length of v, according to its type: // -// Array: the number of elements in v. -// Pointer to array: the number of elements in *v (even if v is nil). -// Slice, or map: the number of elements in v; if v is nil, len(v) is zero. -// String: the number of bytes in v. -// Channel: the number of elements queued (unread) in the channel buffer; -// if v is nil, len(v) is zero. +// - Array: the number of elements in v. +// - Pointer to array: the number of elements in *v (even if v is nil). +// - Slice, or map: the number of elements in v; if v is nil, len(v) is zero. +// - String: the number of bytes in v. +// - Channel: the number of elements queued (unread) in the channel buffer; +// if v is nil, len(v) is zero. // // For some arguments, such as a string literal or a simple array expression, the // result can be a constant. See the Go language specification's "Length and @@ -176,12 +176,12 @@ func len(v Type) int // The cap built-in function returns the capacity of v, according to its type: // -// Array: the number of elements in v (same as len(v)). -// Pointer to array: the number of elements in *v (same as len(v)). -// Slice: the maximum length the slice can reach when resliced; -// if v is nil, cap(v) is zero. -// Channel: the channel buffer capacity, in units of elements; -// if v is nil, cap(v) is zero. +// - Array: the number of elements in v (same as len(v)). +// - Pointer to array: the number of elements in *v (same as len(v)). +// - Slice: the maximum length the slice can reach when resliced; +// if v is nil, cap(v) is zero. +// - Channel: the channel buffer capacity, in units of elements; +// if v is nil, cap(v) is zero. // // For some arguments, such as a simple array expression, the result can be a // constant. See the Go language specification's "Length and capacity" section for @@ -194,18 +194,18 @@ func cap(v Type) int // argument, not a pointer to it. The specification of the result depends on // the type: // -// Slice: The size specifies the length. The capacity of the slice is -// equal to its length. A second integer argument may be provided to -// specify a different capacity; it must be no smaller than the -// length. For example, make([]int, 0, 10) allocates an underlying array -// of size 10 and returns a slice of length 0 and capacity 10 that is -// backed by this underlying array. -// Map: An empty map is allocated with enough space to hold the -// specified number of elements. The size may be omitted, in which case -// a small starting size is allocated. -// Channel: The channel's buffer is initialized with the specified -// buffer capacity. If zero, or the size is omitted, the channel is -// unbuffered. +// - Slice: The size specifies the length. The capacity of the slice is +// equal to its length. A second integer argument may be provided to +// specify a different capacity; it must be no smaller than the +// length. For example, make([]int, 0, 10) allocates an underlying array +// of size 10 and returns a slice of length 0 and capacity 10 that is +// backed by this underlying array. +// - Map: An empty map is allocated with enough space to hold the +// specified number of elements. The size may be omitted, in which case +// a small starting size is allocated. +// - Channel: The channel's buffer is initialized with the specified +// buffer capacity. If zero, or the size is omitted, the channel is +// unbuffered. func make(t Type, size ...IntegerType) Type // The max built-in function returns the largest value of a fixed number of @@ -247,7 +247,7 @@ func imag(c ComplexType) FloatType // to the zero value of the respective element type. If the argument // type is a type parameter, the type parameter's type set must // contain only map or slice types, and clear performs the operation -// implied by the type argument. +// implied by the type argument. If t is nil, clear is a no-op. func clear[T ~[]Type | ~map[Type]Type1](t T) // The close built-in function closes a channel, which must be either diff --git a/src/bytes/example_test.go b/src/bytes/example_test.go index c9086d391809d4..71a4a9e2ca2f23 100644 --- a/src/bytes/example_test.go +++ b/src/bytes/example_test.go @@ -628,3 +628,93 @@ func ExampleToUpperSpecial() { // Original : ahoj vývojári golang // ToUpper : AHOJ VÝVOJÁRİ GOLANG } + +func ExampleLines() { + text := []byte("Hello\nWorld\nGo Programming\n") + for line := range bytes.Lines(text) { + fmt.Printf("%q\n", line) + } + + // Output: + // "Hello\n" + // "World\n" + // "Go Programming\n" +} + +func ExampleSplitSeq() { + s := []byte("a,b,c,d") + for part := range bytes.SplitSeq(s, []byte(",")) { + fmt.Printf("%q\n", part) + } + + // Output: + // "a" + // "b" + // "c" + // "d" +} + +func ExampleSplitAfterSeq() { + s := []byte("a,b,c,d") + for part := range bytes.SplitAfterSeq(s, []byte(",")) { + fmt.Printf("%q\n", part) + } + + // Output: + // "a," + // "b," + // "c," + // "d" +} + +func ExampleFieldsSeq() { + text := []byte("The quick brown fox") + fmt.Println("Split byte slice into fields:") + for word := range bytes.FieldsSeq(text) { + fmt.Printf("%q\n", word) + } + + textWithSpaces := []byte(" lots of spaces ") + fmt.Println("\nSplit byte slice with multiple spaces:") + for word := range bytes.FieldsSeq(textWithSpaces) { + fmt.Printf("%q\n", word) + } + + // Output: + // Split byte slice into fields: + // "The" + // "quick" + // "brown" + // "fox" + // + // Split byte slice with multiple spaces: + // "lots" + // "of" + // "spaces" +} + +func ExampleFieldsFuncSeq() { + text := []byte("The quick brown fox") + fmt.Println("Split on whitespace(similar to FieldsSeq):") + for word := range bytes.FieldsFuncSeq(text, unicode.IsSpace) { + fmt.Printf("%q\n", word) + } + + mixedText := []byte("abc123def456ghi") + fmt.Println("\nSplit on digits:") + for word := range bytes.FieldsFuncSeq(mixedText, unicode.IsDigit) { + fmt.Printf("%q\n", word) + } + + // Output: + // Split on whitespace(similar to FieldsSeq): + // "The" + // "quick" + // "brown" + // "fox" + // + // Split on digits: + // "abc" + // "def" + // "ghi" +} diff --git a/src/bytes/iter.go b/src/bytes/iter.go index 1cf13a94ec263e..799602d9e3667d 100644 --- a/src/bytes/iter.go +++ b/src/bytes/iter.go @@ -67,26 +67,26 @@ func splitSeq(s, sep []byte, sepSave int) iter.Seq[[]byte] { } } -// SplitSeq returns an iterator over all substrings of s separated by sep. -// The iterator yields the same strings that would be returned by Split(s, sep), -// but without constructing the slice. +// SplitSeq returns an iterator over all subslices of s separated by sep. +// The iterator yields the same subslices that would be returned by [Split](s, sep), +// but without constructing a new slice containing the subslices. // It returns a single-use iterator. func SplitSeq(s, sep []byte) iter.Seq[[]byte] { return splitSeq(s, sep, 0) } -// SplitAfterSeq returns an iterator over substrings of s split after each instance of sep. -// The iterator yields the same strings that would be returned by SplitAfter(s, sep), -// but without constructing the slice. +// SplitAfterSeq returns an iterator over subslices of s split after each instance of sep. +// The iterator yields the same subslices that would be returned by [SplitAfter](s, sep), +// but without constructing a new slice containing the subslices. // It returns a single-use iterator. func SplitAfterSeq(s, sep []byte) iter.Seq[[]byte] { return splitSeq(s, sep, len(sep)) } -// FieldsSeq returns an iterator over substrings of s split around runs of -// whitespace characters, as defined by unicode.IsSpace. -// The iterator yields the same strings that would be returned by Fields(s), -// but without constructing the slice. +// FieldsSeq returns an iterator over subslices of s split around runs of +// whitespace characters, as defined by [unicode.IsSpace]. +// The iterator yields the same subslices that would be returned by [Fields](s), +// but without constructing a new slice containing the subslices. func FieldsSeq(s []byte) iter.Seq[[]byte] { return func(yield func([]byte) bool) { start := -1 @@ -116,10 +116,10 @@ func FieldsSeq(s []byte) iter.Seq[[]byte] { } } -// FieldsFuncSeq returns an iterator over substrings of s split around runs of +// FieldsFuncSeq returns an iterator over subslices of s split around runs of // Unicode code points satisfying f(c). -// The iterator yields the same strings that would be returned by FieldsFunc(s), -// but without constructing the slice. +// The iterator yields the same subslices that would be returned by [FieldsFunc](s), +// but without constructing a new slice containing the subslices. func FieldsFuncSeq(s []byte, f func(rune) bool) iter.Seq[[]byte] { return func(yield func([]byte) bool) { start := -1 diff --git a/src/clean.bat b/src/clean.bat index 6688b41e5e2c9d..2e03806305b433 100644 --- a/src/clean.bat +++ b/src/clean.bat @@ -6,27 +6,16 @@ setlocal -set GOBUILDFAIL=0 - -go tool dist env -w -p >env.bat -if errorlevel 1 goto fail +go tool dist env -w -p >env.bat || exit /b 1 call .\env.bat del env.bat echo. -if exist %GOTOOLDIR%\dist.exe goto distok -echo cannot find %GOTOOLDIR%\dist; nothing to clean -goto fail -:distok +if not exist %GOTOOLDIR%\dist.exe ( + echo cannot find %GOTOOLDIR%\dist.exe; nothing to clean + exit /b 1 +) "%GOBIN%\go" clean -i std "%GOBIN%\go" tool dist clean "%GOBIN%\go" clean -i cmd - -goto end - -:fail -set GOBUILDFAIL=1 - -:end -if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL% diff --git a/src/cmd/api/api_test.go b/src/cmd/api/api_test.go index 78482333330bd8..32da68982bce45 100644 --- a/src/cmd/api/api_test.go +++ b/src/cmd/api/api_test.go @@ -57,7 +57,10 @@ func TestGolden(t *testing.T) { // TODO(gri) remove extra pkg directory eventually goldenFile := filepath.Join("testdata", "src", "pkg", fi.Name(), "golden.txt") w := NewWalker(nil, "testdata/src/pkg") - pkg, _ := w.import_(fi.Name()) + pkg, err := w.import_(fi.Name()) + if err != nil { + t.Fatalf("import %s: %v", fi.Name(), err) + } w.export(pkg) if *updateGolden { @@ -201,7 +204,13 @@ func BenchmarkAll(b *testing.B) { for _, context := range contexts { w := NewWalker(context, filepath.Join(testenv.GOROOT(b), "src")) for _, name := range w.stdPackages { - pkg, _ := w.import_(name) + pkg, err := w.import_(name) + if _, nogo := err.(*build.NoGoError); nogo { + continue + } + if err != nil { + b.Fatalf("import %s (%s-%s): %v", name, context.GOOS, context.GOARCH, err) + } w.export(pkg) } w.Features() @@ -239,8 +248,7 @@ func TestIssue21181(t *testing.T) { w := NewWalker(context, "testdata/src/issue21181") pkg, err := w.import_("p") if err != nil { - t.Fatalf("%s: (%s-%s) %s %v", err, context.GOOS, context.GOARCH, - pkg.Name(), w.imported) + t.Fatalf("import %s (%s-%s): %v", "p", context.GOOS, context.GOARCH, err) } w.export(pkg) } diff --git a/src/cmd/asm/internal/arch/arm64.go b/src/cmd/asm/internal/arch/arm64.go index e63601de6476f0..87ccb8c0409313 100644 --- a/src/cmd/asm/internal/arch/arm64.go +++ b/src/cmd/asm/internal/arch/arm64.go @@ -59,10 +59,10 @@ func jumpArm64(word string) bool { var arm64SpecialOperand map[string]arm64.SpecialOperand -// GetARM64SpecialOperand returns the internal representation of a special operand. -func GetARM64SpecialOperand(name string) arm64.SpecialOperand { +// ARM64SpecialOperand returns the internal representation of a special operand. +func ARM64SpecialOperand(name string) arm64.SpecialOperand { if arm64SpecialOperand == nil { - // Generate the mapping automatically when the first time the function is called. + // Generate mapping when function is first called. arm64SpecialOperand = map[string]arm64.SpecialOperand{} for opd := arm64.SPOP_BEGIN; opd < arm64.SPOP_END; opd++ { arm64SpecialOperand[opd.String()] = opd diff --git a/src/cmd/asm/internal/arch/riscv64.go b/src/cmd/asm/internal/arch/riscv64.go index 27a66c5e637bb7..69e060a865ed7b 100644 --- a/src/cmd/asm/internal/arch/riscv64.go +++ b/src/cmd/asm/internal/arch/riscv64.go @@ -13,9 +13,8 @@ import ( "cmd/internal/obj/riscv" ) -// IsRISCV64AMO reports whether the op (as defined by a riscv.A* -// constant) is one of the AMO instructions that requires special -// handling. +// IsRISCV64AMO reports whether op is an AMO instruction that requires +// special handling. func IsRISCV64AMO(op obj.As) bool { switch op { case riscv.ASCW, riscv.ASCD, riscv.AAMOSWAPW, riscv.AAMOSWAPD, riscv.AAMOADDW, riscv.AAMOADDD, @@ -26,3 +25,33 @@ func IsRISCV64AMO(op obj.As) bool { } return false } + +// IsRISCV64VTypeI reports whether op is a vtype immediate instruction that +// requires special handling. +func IsRISCV64VTypeI(op obj.As) bool { + return op == riscv.AVSETVLI || op == riscv.AVSETIVLI +} + +var riscv64SpecialOperand map[string]riscv.SpecialOperand + +// RISCV64SpecialOperand returns the internal representation of a special operand. +func RISCV64SpecialOperand(name string) riscv.SpecialOperand { + if riscv64SpecialOperand == nil { + // Generate mapping when function is first called. + riscv64SpecialOperand = map[string]riscv.SpecialOperand{} + for opd := riscv.SPOP_BEGIN; opd < riscv.SPOP_END; opd++ { + riscv64SpecialOperand[opd.String()] = opd + } + } + if opd, ok := riscv64SpecialOperand[name]; ok { + return opd + } + return riscv.SPOP_END +} + +// RISCV64ValidateVectorType reports whether the given configuration is a +// valid vector type. +func RISCV64ValidateVectorType(vsew, vlmul, vtail, vmask int64) error { + _, err := riscv.EncodeVectorType(vsew, vlmul, vtail, vmask) + return err +} diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index 9fc7fa559816cb..a1f6a73d708103 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -915,6 +915,19 @@ func (p *Parser) asmInstruction(op obj.As, cond string, a []obj.Addr) { prog.To = a[5] break } + if p.arch.Family == sys.RISCV64 && arch.IsRISCV64VTypeI(op) { + prog.From = a[0] + vsew := p.getSpecial(prog, op, &a[1]) + vlmul := p.getSpecial(prog, op, &a[2]) + vtail := p.getSpecial(prog, op, &a[3]) + vmask := p.getSpecial(prog, op, &a[4]) + if err := arch.RISCV64ValidateVectorType(vsew, vlmul, vtail, vmask); err != nil { + p.errorf("invalid vtype: %v", err) + } + prog.AddRestSourceArgs([]obj.Addr{a[1], a[2], a[3], a[4]}) + prog.To = a[5] + break + } fallthrough default: p.errorf("can't handle %s instruction with %d operands", op, len(a)) @@ -965,3 +978,11 @@ func (p *Parser) getRegister(prog *obj.Prog, op obj.As, addr *obj.Addr) int16 { } return addr.Reg } + +// getSpecial checks that addr represents a special operand and returns its value. +func (p *Parser) getSpecial(prog *obj.Prog, op obj.As, addr *obj.Addr) int64 { + if addr.Type != obj.TYPE_SPECIAL || addr.Name != 0 || addr.Reg != 0 || addr.Index != 0 { + p.errorf("%s: expected special operand; found %s", op, obj.Dconv(prog, addr)) + } + return addr.Offset +} diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go index 638f4e2fc4e7fe..8f8f6dcc346a44 100644 --- a/src/cmd/asm/internal/asm/parse.go +++ b/src/cmd/asm/internal/asm/parse.go @@ -21,6 +21,7 @@ import ( "cmd/asm/internal/lex" "cmd/internal/obj" "cmd/internal/obj/arm64" + "cmd/internal/obj/riscv" "cmd/internal/obj/x86" "cmd/internal/objabi" "cmd/internal/src" @@ -398,16 +399,21 @@ func (p *Parser) operand(a *obj.Addr) { tok := p.next() name := tok.String() if tok.ScanToken == scanner.Ident && !p.atStartOfRegister(name) { + // See if this is an architecture specific special operand. switch p.arch.Family { case sys.ARM64: - // arm64 special operands. - if opd := arch.GetARM64SpecialOperand(name); opd != arm64.SPOP_END { + if opd := arch.ARM64SpecialOperand(name); opd != arm64.SPOP_END { a.Type = obj.TYPE_SPECIAL a.Offset = int64(opd) - break } - fallthrough - default: + case sys.RISCV64: + if opd := arch.RISCV64SpecialOperand(name); opd != riscv.SPOP_END { + a.Type = obj.TYPE_SPECIAL + a.Offset = int64(opd) + } + } + + if a.Type != obj.TYPE_SPECIAL { // We have a symbol. Parse $sym±offset(symkind) p.symbolReference(a, p.qualifySymbol(name), prefix) } diff --git a/src/cmd/asm/internal/asm/testdata/riscv64.s b/src/cmd/asm/internal/asm/testdata/riscv64.s index 37c0c1d858f34c..fc44f561f259ab 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64.s @@ -363,6 +363,10 @@ start: SLLIUW $63, X17, X18 // 1b99f80b SLLIUW $1, X18, X19 // 9b191908 + // + // "B" Extension for Bit Manipulation, Version 1.0.0 + // + // 28.4.2: Basic Bit Manipulation (Zbb) ANDN X19, X20, X21 // b37a3a41 or 93caf9ffb37a5a01 ANDN X19, X20 // 337a3a41 or 93cff9ff337afa01 @@ -420,6 +424,30 @@ start: BSET $63, X9 // 9394f42b BSETI $1, X10, X11 // 93151528 + // + // "V" Standard Extension for Vector Operations, Version 1.0 + // + + // 31.6: Configuration Setting Instructions + VSETVLI X10, E8, M1, TU, MU, X12 // 57760500 + VSETVLI X10, E16, M1, TU, MU, X12 // 57768500 + VSETVLI X10, E32, M1, TU, MU, X12 // 57760501 + VSETVLI X10, E64, M1, TU, MU, X12 // 57768501 + VSETVLI X10, E32, M1, TU, MA, X12 // 57760509 + VSETVLI X10, E32, M1, TA, MA, X12 // 5776050d + VSETVLI X10, E32, M2, TA, MA, X12 // 5776150d + VSETVLI X10, E32, M4, TA, MA, X12 // 5776250d + VSETVLI X10, E32, M8, TA, MA, X12 // 5776350d + VSETVLI X10, E32, MF2, TA, MA, X12 // 5776550d + VSETVLI X10, E32, MF4, TA, MA, X12 // 5776650d + VSETVLI X10, E32, MF8, TA, MA, X12 // 5776750d + VSETVLI X10, E32, M1, TA, MA, X12 // 5776050d + VSETVLI $15, E32, M1, TA, MA, X12 // 57f607cd + VSETIVLI $0, E32, M1, TA, MA, X12 // 577600cd + VSETIVLI $15, E32, M1, TA, MA, X12 // 57f607cd + VSETIVLI $31, E32, M1, TA, MA, X12 // 57f60fcd + VSETVL X10, X11, X12 // 57f6a580 + // // Privileged ISA // @@ -482,6 +510,9 @@ start: MOVD F0, 4(X5) // 27b20200 MOVD F0, F1 // d3000022 + // Convert to load of symbol (AUIPC + FLD) + MOVD $(709.78271289338397), F3 // 970f000087b10f00 + // TLS load with local-exec (LUI + ADDIW + ADD of TP + load) MOV tls(SB), X5 // b70f00009b8f0f00b38f4f0083b20f00 MOVB tls(SB), X5 // b70f00009b8f0f00b38f4f0083820f00 diff --git a/src/cmd/asm/internal/asm/testdata/riscv64error.s b/src/cmd/asm/internal/asm/testdata/riscv64error.s index 0b0184aaa7e1ed..a90f22af9f40b8 100644 --- a/src/cmd/asm/internal/asm/testdata/riscv64error.s +++ b/src/cmd/asm/internal/asm/testdata/riscv64error.s @@ -46,4 +46,8 @@ TEXT errors(SB),$0 SRLI $1, X5, F1 // ERROR "expected integer register in rd position but got non-integer register F1" SRLI $1, F1, X5 // ERROR "expected integer register in rs1 position but got non-integer register F1" FNES F1, (X5) // ERROR "needs an integer register output" + VSETVLI $32, E16, M1, TU, MU, X12 // ERROR "must be in range [0, 31] (5 bits)" + VSETVLI $-1, E32, M2, TA, MA, X12 // ERROR "must be in range [0, 31] (5 bits)" + VSETIVLI X10, E32, M2, TA, MA, X12 // ERROR "expected immediate value" + VSETVL X10, X11 // ERROR "expected integer register in rs1 position" RET diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go index 9ff5fd41901c90..ef5272299bbf07 100644 --- a/src/cmd/cgo/doc.go +++ b/src/cmd/cgo/doc.go @@ -796,7 +796,7 @@ Instead, the build process generates an object file using dynamic linkage to the desired libraries. The main function is provided by _cgo_main.c: - int main() { return 0; } + int main(int argc, char **argv) { return 0; } void crosscall2(void(*fn)(void*), void *a, int c, uintptr_t ctxt) { } uintptr_t _cgo_wait_runtime_init_done(void) { return 0; } void _cgo_release_context(uintptr_t ctxt) { } diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go index b098895c299fbc..9fe5e791feb16f 100644 --- a/src/cmd/cgo/internal/testcshared/cshared_test.go +++ b/src/cmd/cgo/internal/testcshared/cshared_test.go @@ -906,7 +906,7 @@ func TestIssue68411(t *testing.T) { } var found int - for _, line := range bytes.Split(data, []byte("\n")) { + for line := range bytes.Lines(data) { for _, fn := range funcs { if bytes.Contains(line, []byte(fn.name)) { found++ diff --git a/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go index a761cc38985bbf..6a2be8b53c5415 100644 --- a/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go +++ b/src/cmd/cgo/internal/testcshared/testdata/issue68411/issue68411.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Go Authors. All rights reserved. +// Copyright 2025 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. diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go index 939e282ff069ed..77beb0992cf74f 100644 --- a/src/cmd/cgo/main.go +++ b/src/cmd/cgo/main.go @@ -390,7 +390,7 @@ func main() { // We already put _cgo_ at the beginning, so the main // concern is other cgo wrappers for the same functions. // Use the beginning of the 16 bytes hash of the input to disambiguate. - h := hash.New16() + h := hash.New32() io.WriteString(h, *importPath) var once sync.Once var wg sync.WaitGroup diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index d53bbe9cdfde4e..43c30a200039a7 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -59,7 +59,7 @@ func (p *Package) writeDefs() { // Write C main file for using gcc to resolve imports. fmt.Fprintf(fm, "#include \n") // For size_t below. - fmt.Fprintf(fm, "int main() { return 0; }\n") + fmt.Fprintf(fm, "int main(int argc __attribute__((unused)), char **argv __attribute__((unused))) { return 0; }\n") if *importRuntimeCgo { fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), size_t ctxt __attribute__((unused))) { }\n") fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void) { return 0; }\n") @@ -942,7 +942,9 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { // just have to agree. The gcc struct will be compiled // with __attribute__((packed)) so all padding must be // accounted for explicitly. - ctype := "struct {\n" + var ctype strings.Builder + const start = "struct {\n" + ctype.WriteString(start) gotype := new(bytes.Buffer) fmt.Fprintf(gotype, "struct {\n") off := int64(0) @@ -952,11 +954,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { t := p.cgoType(typ) if off%t.Align != 0 { pad := t.Align - off%t.Align - ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad) + fmt.Fprintf(&ctype, "\t\tchar __pad%d[%d];\n", npad, pad) off += pad npad++ } - ctype += fmt.Sprintf("\t\t%s %s;\n", t.C, name) + fmt.Fprintf(&ctype, "\t\t%s %s;\n", t.C, name) fmt.Fprintf(gotype, "\t\t%s ", name) noSourceConf.Fprint(gotype, fset, typ) fmt.Fprintf(gotype, "\n") @@ -974,10 +976,10 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { func(i int, aname string, atype ast.Expr) { argField(atype, "r%d", i) }) - if ctype == "struct {\n" { - ctype += "\t\tchar unused;\n" // avoid empty struct + if ctype.Len() == len(start) { + ctype.WriteString("\t\tchar unused;\n") // avoid empty struct } - ctype += "\t}" + ctype.WriteString("\t}") fmt.Fprintf(gotype, "\t}") // Get the return type of the wrapper function @@ -1007,24 +1009,25 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { if goos == "windows" { gccExport = "__declspec(dllexport) " } - s := fmt.Sprintf("%s%s %s(", gccExport, gccResult, exp.ExpName) + var s strings.Builder + fmt.Fprintf(&s, "%s%s %s(", gccExport, gccResult, exp.ExpName) if fn.Recv != nil { - s += p.cgoType(fn.Recv.List[0].Type).C.String() - s += " recv" + s.WriteString(p.cgoType(fn.Recv.List[0].Type).C.String()) + s.WriteString(" recv") } if len(fntype.Params.List) > 0 { forFieldList(fntype.Params, func(i int, aname string, atype ast.Expr) { if i > 0 || fn.Recv != nil { - s += ", " + s.WriteString(", ") } - s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i)) + fmt.Fprintf(&s, "%s %s", p.cgoType(atype).C, exportParamName(aname, i)) }) } else { - s += "void" + s.WriteString("void") } - s += ")" + s.WriteByte(')') if len(exp.Doc) > 0 { fmt.Fprintf(fgcch, "\n%s", exp.Doc) @@ -1032,11 +1035,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprint(fgcch, "\n") } } - fmt.Fprintf(fgcch, "extern %s;\n", s) + fmt.Fprintf(fgcch, "extern %s;\n", s.String()) fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *);\n", cPrefix, exp.ExpName) fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD") - fmt.Fprintf(fgcc, "\n%s\n", s) + fmt.Fprintf(fgcc, "\n%s\n", s.String()) fmt.Fprintf(fgcc, "{\n") fmt.Fprintf(fgcc, "\tsize_t _cgo_ctxt = _cgo_wait_runtime_init_done();\n") // The results part of the argument structure must be @@ -1048,7 +1051,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { // string.h for memset, and is also robust to C++ // types with constructors. Both GCC and LLVM optimize // this into just zeroing _cgo_a. - fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype, p.packedAttribute()) + fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype.String(), p.packedAttribute()) fmt.Fprintf(fgcc, "\tstatic _cgo_argtype _cgo_zero;\n") fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _cgo_zero;\n") if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) { @@ -1099,7 +1102,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { // This unpacks the argument struct above and calls the Go function. fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype) - fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName) + fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p __attribute__((unused))){}\n", cPrefix, exp.ExpName) fmt.Fprintf(fgo2, "\t") @@ -1943,6 +1946,8 @@ const builtinExportProlog = ` #ifndef GO_CGO_GOSTRING_TYPEDEF typedef struct { const char *p; ptrdiff_t n; } _GoString_; +extern size_t _GoStringLen(_GoString_ s); +extern const char *_GoStringPtr(_GoString_ s); #endif #endif diff --git a/src/cmd/compile/doc.go b/src/cmd/compile/doc.go index f45df3f86a671d..49abb857adfe13 100644 --- a/src/cmd/compile/doc.go +++ b/src/cmd/compile/doc.go @@ -15,7 +15,7 @@ the package and about types used by symbols imported by the package from other packages. It is therefore not necessary when compiling client C of package P to read the files of P's dependencies, only the compiled output of P. -Command Line +# Command Line Usage: @@ -150,14 +150,21 @@ Flags to debug the compiler itself: -w Debug type checking. -Compiler Directives +# Compiler Directives The compiler accepts directives in the form of comments. -To distinguish them from non-directive comments, directives -require no space between the comment opening and the name of the directive. However, since -they are comments, tools unaware of the directive convention or of a particular +Each directive must be placed its own line, with only leading spaces and tabs +allowed before the comment, and there must be no space between the comment +opening and the name of the directive, to distinguish it from a regular comment. +Tools unaware of the directive convention or of a particular directive can skip over a directive like any other comment. + +Other than the line directive, which is a historical special case; +all other compiler directives are of the form +//go:name, indicating that they are defined by the Go toolchain. */ +// # Line Directives +// // Line directives come in several forms: // // //line :line @@ -197,12 +204,9 @@ directive can skip over a directive like any other comment. // Line directives typically appear in machine-generated code, so that compilers and debuggers // will report positions in the original input to the generator. /* -The line directive is a historical special case; all other directives are of the form -//go:name, indicating that they are defined by the Go toolchain. -Each directive must be placed its own line, with only leading spaces and tabs -allowed before the comment. -Each directive applies to the Go code that immediately follows it, -which typically must be a declaration. +# Function Directives + +A function directive applies to the Go function that immediately follows it. //go:noescape @@ -245,6 +249,8 @@ It specifies that the function must omit its usual stack overflow check. This is most commonly used by low-level runtime code invoked at times when it is unsafe for the calling goroutine to be preempted. +# Linkname Directive + //go:linkname localname [importpath.name] The //go:linkname directive conventionally precedes the var or func @@ -295,17 +301,34 @@ The declaration of lower.f may also have a linkname directive with a single argument, f. This is optional, but helps alert the reader that the function is accessed from outside the package. +# WebAssembly Directives + //go:wasmimport importmodule importname The //go:wasmimport directive is wasm-only and must be followed by a -function declaration. +function declaration with no body. It specifies that the function is provided by a wasm module identified -by ``importmodule`` and ``importname``. +by ``importmodule'' and ``importname''. For example, //go:wasmimport a_module f func g() -The types of parameters and return values to the Go function are translated to +causes g to refer to the WebAssembly function f from module a_module. + + //go:wasmexport exportname + +The //go:wasmexport directive is wasm-only and must be followed by a +function definition. +It specifies that the function is exported to the wasm host as ``exportname''. +For example, + + //go:wasmexport h + func hWasm() { ... } + +make Go function hWasm available outside this WebAssembly module as h. + +For both go:wasmimport and go:wasmexport, +the types of parameters and return values to the Go function are translated to Wasm according to the following table: Go types Wasm types @@ -318,24 +341,12 @@ Wasm according to the following table: pointer i32 (more restrictions below) string (i32, i32) (only permitted as a parameters, not a result) +Any other parameter types are disallowed by the compiler. + For a pointer type, its element type must be a bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, an array whose element type is a permitted pointer element type, or a struct, which, if non-empty, embeds -structs.HostLayout, and contains only fields whose types are permitted pointer +[structs.HostLayout], and contains only fields whose types are permitted pointer element types. - -Any other parameter types are disallowed by the compiler. - - //go:wasmexport exportname - -The //go:wasmexport directive is wasm-only and must be followed by a -function definition. -It specifies that the function is exported to the wasm host as ``exportname``. - - //go:wasmexport f - func g() - -The types of parameters and return values to the Go function are permitted and -translated to Wasm in the same way as //go:wasmimport functions. */ package main diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index 493369af51ce15..9eef71f760357a 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -202,7 +202,7 @@ func getgFromTLS(s *ssagen.State, r int16) { func ssaGenValue(s *ssagen.State, v *ssa.Value) { switch v.Op { - case ssa.OpAMD64VFMADD231SD: + case ssa.OpAMD64VFMADD231SD, ssa.OpAMD64VFMADD231SS: p := s.Prog(v.Op.Asm()) p.From = obj.Addr{Type: obj.TYPE_REG, Reg: v.Args[2].Reg()} p.To = obj.Addr{Type: obj.TYPE_REG, Reg: v.Reg()} @@ -1170,6 +1170,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { case ssa.OpAMD64BSFL, ssa.OpAMD64BSRL, ssa.OpAMD64SQRTSD, ssa.OpAMD64SQRTSS: p.To.Reg = v.Reg() } + case ssa.OpAMD64LoweredRound32F, ssa.OpAMD64LoweredRound64F: + // input is already rounded case ssa.OpAMD64ROUNDSD: p := s.Prog(v.Op.Asm()) val := v.AuxInt diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go index adcabb1b954aeb..0f5c5a17bd5fa2 100644 --- a/src/cmd/compile/internal/arm64/ssa.go +++ b/src/cmd/compile/internal/arm64/ssa.go @@ -516,7 +516,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssagen.AddAux(&p.From, v) p.To.Type = obj.TYPE_REG p.To.Reg = v.Reg() - case ssa.OpARM64LDP: + case ssa.OpARM64LDP, ssa.OpARM64LDPW, ssa.OpARM64LDPSW, ssa.OpARM64FLDPD, ssa.OpARM64FLDPS: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_MEM p.From.Reg = v.Args[0].Reg() @@ -583,7 +583,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.To = genIndexedOperand(v.Op, v.Args[0].Reg(), v.Args[1].Reg()) p.From.Type = obj.TYPE_REG p.From.Reg = v.Args[2].Reg() - case ssa.OpARM64STP: + case ssa.OpARM64STP, ssa.OpARM64STPW, ssa.OpARM64FSTPD, ssa.OpARM64FSTPS: p := s.Prog(v.Op.Asm()) p.From.Type = obj.TYPE_REGREG p.From.Reg = v.Args[1].Reg() diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index c93f008ba2c26b..9afbeb9d3b5c91 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -47,5 +47,7 @@ func dumpasmhdr() { } } - b.Close() + if err := b.Close(); err != nil { + base.Fatalf("%v", err) + } } diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go index 4b42c81ef81845..37bbce03189c24 100644 --- a/src/cmd/compile/internal/gc/obj.go +++ b/src/cmd/compile/internal/gc/obj.go @@ -57,7 +57,7 @@ func dumpobj1(outfile string, mode int) { fmt.Printf("can't create %s: %v\n", outfile, err) base.ErrorExit() } - defer bout.Close() + bout.WriteString("!\n") if mode&modeCompilerObj != 0 { @@ -70,6 +70,12 @@ func dumpobj1(outfile string, mode int) { dumpLinkerObj(bout) finishArchiveEntry(bout, start, "_go_.o") } + + if err := bout.Close(); err != nil { + base.FlushErrors() + fmt.Printf("error while writing to file %s: %v\n", outfile, err) + base.ErrorExit() + } } func printObjHeader(bout *bio.Writer) { diff --git a/src/cmd/compile/internal/liveness/mergelocals.go b/src/cmd/compile/internal/liveness/mergelocals.go index cbe49aa6550838..6967ee016ee710 100644 --- a/src/cmd/compile/internal/liveness/mergelocals.go +++ b/src/cmd/compile/internal/liveness/mergelocals.go @@ -56,7 +56,7 @@ type candRegion struct { type cstate struct { fn *ir.Func f *ssa.Func - lv *liveness + lv *Liveness cands []*ir.Name nameToSlot map[*ir.Name]int32 regions []candRegion diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go index a20d856aa2a77e..6c97858cf6efbb 100644 --- a/src/cmd/compile/internal/liveness/plive.go +++ b/src/cmd/compile/internal/liveness/plive.go @@ -102,8 +102,8 @@ type blockEffects struct { liveout bitvec.BitVec } -// A collection of global state used by liveness analysis. -type liveness struct { +// A collection of global state used by Liveness analysis. +type Liveness struct { fn *ir.Func f *ssa.Func vars []*ir.Name @@ -235,7 +235,7 @@ func getvariables(fn *ir.Func) ([]*ir.Name, map[*ir.Name]int32) { return vars, idx } -func (lv *liveness) initcache() { +func (lv *Liveness) initcache() { if lv.cache.initialized { base.Fatalf("liveness cache initialized twice") return @@ -281,7 +281,7 @@ const ( // valueEffects returns the index of a variable in lv.vars and the // liveness effects v has on that variable. // If v does not affect any tracked variables, it returns -1, 0. -func (lv *liveness) valueEffects(v *ssa.Value) (int32, liveEffect) { +func (lv *Liveness) valueEffects(v *ssa.Value) (int32, liveEffect) { n, e := affectedVar(v) if e == 0 || n == nil { // cheapest checks first return -1, 0 @@ -392,8 +392,8 @@ type livenessFuncCache struct { // Constructs a new liveness structure used to hold the global state of the // liveness computation. The cfg argument is a slice of *BasicBlocks and the // vars argument is a slice of *Nodes. -func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *liveness { - lv := &liveness{ +func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int32, stkptrsize int64) *Liveness { + lv := &Liveness{ fn: fn, f: f, vars: vars, @@ -447,14 +447,14 @@ func newliveness(fn *ir.Func, f *ssa.Func, vars []*ir.Name, idx map[*ir.Name]int return lv } -func (lv *liveness) blockEffects(b *ssa.Block) *blockEffects { +func (lv *Liveness) blockEffects(b *ssa.Block) *blockEffects { return &lv.be[b.ID] } // Generates live pointer value maps for arguments and local variables. The // this argument and the in arguments are always assumed live. The vars // argument is a slice of *Nodes. -func (lv *liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) { +func (lv *Liveness) pointerMap(liveout bitvec.BitVec, vars []*ir.Name, args, locals bitvec.BitVec) { var slotsSeen map[int64]*ir.Name checkForDuplicateSlots := base.Debug.MergeLocals != 0 if checkForDuplicateSlots { @@ -504,7 +504,7 @@ func IsUnsafe(f *ssa.Func) bool { } // markUnsafePoints finds unsafe points and computes lv.unsafePoints. -func (lv *liveness) markUnsafePoints() { +func (lv *Liveness) markUnsafePoints() { if IsUnsafe(lv.f) { // No complex analysis necessary. lv.allUnsafe = true @@ -647,7 +647,7 @@ func (lv *liveness) markUnsafePoints() { // This does not necessarily mean the instruction is a safe-point. In // particular, call Values can have a stack map in case the callee // grows the stack, but not themselves be a safe-point. -func (lv *liveness) hasStackMap(v *ssa.Value) bool { +func (lv *Liveness) hasStackMap(v *ssa.Value) bool { if !v.Op.IsCall() { return false } @@ -663,7 +663,7 @@ func (lv *liveness) hasStackMap(v *ssa.Value) bool { // Initializes the sets for solving the live variables. Visits all the // instructions in each basic block to summarizes the information at each basic // block -func (lv *liveness) prologue() { +func (lv *Liveness) prologue() { lv.initcache() for _, b := range lv.f.Blocks { @@ -685,7 +685,7 @@ func (lv *liveness) prologue() { } // Solve the liveness dataflow equations. -func (lv *liveness) solve() { +func (lv *Liveness) solve() { // These temporary bitvectors exist to avoid successive allocations and // frees within the loop. nvars := int32(len(lv.vars)) @@ -745,7 +745,7 @@ func (lv *liveness) solve() { // Visits all instructions in a basic block and computes a bit vector of live // variables at each safe point locations. -func (lv *liveness) epilogue() { +func (lv *Liveness) epilogue() { nvars := int32(len(lv.vars)) liveout := bitvec.New(nvars) livedefer := bitvec.New(nvars) // always-live variables @@ -914,7 +914,7 @@ func (lv *liveness) epilogue() { // is actually a net loss: we save about 50k of argument bitmaps but the new // PCDATA tables cost about 100k. So for now we keep using a single index for // both bitmap lists. -func (lv *liveness) compact(b *ssa.Block) { +func (lv *Liveness) compact(b *ssa.Block) { pos := 0 if b == lv.f.Entry { // Handle entry stack map. @@ -939,7 +939,7 @@ func (lv *liveness) compact(b *ssa.Block) { lv.livevars = lv.livevars[:0] } -func (lv *liveness) enableClobber() { +func (lv *Liveness) enableClobber() { // The clobberdead experiment inserts code to clobber pointer slots in all // the dead variables (locals and args) at every synchronous safepoint. if !base.Flag.ClobberDead { @@ -981,7 +981,7 @@ func (lv *liveness) enableClobber() { // Clobber only functions where the hash of the function name matches a pattern. // Useful for binary searching for a miscompiled function. hstr := "" - for _, b := range hash.Sum20([]byte(lv.f.Name)) { + for _, b := range hash.Sum32([]byte(lv.f.Name)) { hstr += fmt.Sprintf("%08b", b) } if !strings.HasSuffix(hstr, h) { @@ -994,7 +994,7 @@ func (lv *liveness) enableClobber() { // Inserts code to clobber pointer slots in all the dead variables (locals and args) // at every synchronous safepoint in b. -func (lv *liveness) clobber(b *ssa.Block) { +func (lv *Liveness) clobber(b *ssa.Block) { // Copy block's values to a temporary. oldSched := append([]*ssa.Value{}, b.Values...) b.Values = b.Values[:0] @@ -1029,7 +1029,7 @@ func (lv *liveness) clobber(b *ssa.Block) { // clobber generates code to clobber pointer slots in all dead variables // (those not marked in live). Clobbering instructions are added to the end // of b.Values. -func clobber(lv *liveness, b *ssa.Block, live bitvec.BitVec) { +func clobber(lv *Liveness, b *ssa.Block, live bitvec.BitVec) { for i, n := range lv.vars { if !live.Get(int32(i)) && !n.Addrtaken() && !n.OpenDeferSlot() && !n.IsOutputParamHeapAddr() { // Don't clobber stack objects (address-taken). They are @@ -1102,7 +1102,7 @@ func clobberPtr(b *ssa.Block, v *ir.Name, offset int64) { b.NewValue0IA(src.NoXPos, ssa.OpClobber, types.TypeVoid, offset, v) } -func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) { +func (lv *Liveness) showlive(v *ssa.Value, live bitvec.BitVec) { if base.Flag.Live == 0 || ir.FuncName(lv.fn) == "init" || strings.HasPrefix(ir.FuncName(lv.fn), ".") { return } @@ -1119,6 +1119,24 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) { return } + pos, s := lv.format(v, live) + + base.WarnfAt(pos, "%s", s) +} + +func (lv *Liveness) Format(v *ssa.Value) string { + if v == nil { + _, s := lv.format(nil, lv.stackMaps[0]) + return s + } + if idx := lv.livenessMap.Get(v); idx.StackMapValid() { + _, s := lv.format(v, lv.stackMaps[idx]) + return s + } + return "" +} + +func (lv *Liveness) format(v *ssa.Value, live bitvec.BitVec) (src.XPos, string) { pos := lv.fn.Nname.Pos() if v != nil { pos = v.Pos @@ -1149,11 +1167,10 @@ func (lv *liveness) showlive(v *ssa.Value, live bitvec.BitVec) { for _, v := range names { s += " " + v } - - base.WarnfAt(pos, "%s", s) + return pos, s } -func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool { +func (lv *Liveness) printbvec(printed bool, name string, live bitvec.BitVec) bool { if live.IsEmpty() { return printed } @@ -1177,7 +1194,7 @@ func (lv *liveness) printbvec(printed bool, name string, live bitvec.BitVec) boo } // printeffect is like printbvec, but for valueEffects. -func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bool { +func (lv *Liveness) printeffect(printed bool, name string, pos int32, x bool) bool { if !x { return printed } @@ -1197,7 +1214,7 @@ func (lv *liveness) printeffect(printed bool, name string, pos int32, x bool) bo // Prints the computed liveness information and inputs, for debugging. // This format synthesizes the information used during the multiple passes // into a single presentation. -func (lv *liveness) printDebug() { +func (lv *Liveness) printDebug() { fmt.Printf("liveness: %s\n", ir.FuncName(lv.fn)) for i, b := range lv.f.Blocks { @@ -1309,7 +1326,7 @@ func (lv *liveness) printDebug() { // first word dumped is the total number of bitmaps. The second word is the // length of the bitmaps. All bitmaps are assumed to be of equal length. The // remaining bytes are the raw bitmaps. -func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) { +func (lv *Liveness) emit() (argsSym, liveSym *obj.LSym) { // Size args bitmaps to be just large enough to hold the largest pointer. // First, find the largest Xoffset node we care about. // (Nodes without pointers aren't in lv.vars; see ShouldTrack.) @@ -1370,7 +1387,7 @@ func (lv *liveness) emit() (argsSym, liveSym *obj.LSym) { // structure read by the garbage collector. // Returns a map from GC safe points to their corresponding stack map index, // and a map that contains all input parameters that may be partially live. -func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map, map[*ir.Name]bool) { +func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs, retLiveness bool) (Map, map[*ir.Name]bool, *Liveness) { // Construct the global liveness state. vars, idx := getvariables(curfn) lv := newliveness(curfn, f, vars, idx, stkptrsize) @@ -1432,10 +1449,15 @@ func Compute(curfn *ir.Func, f *ssa.Func, stkptrsize int64, pp *objw.Progs) (Map p.To.Sym = x } - return lv.livenessMap, lv.partLiveArgs + retLv := lv + if !retLiveness { + retLv = nil + } + + return lv.livenessMap, lv.partLiveArgs, retLv } -func (lv *liveness) emitStackObjects() *obj.LSym { +func (lv *Liveness) emitStackObjects() *obj.LSym { var vars []*ir.Name for _, n := range lv.fn.Dcl { if shouldTrack(n) && n.Addrtaken() && n.Esc() != ir.EscHeap { diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go index 7905c374c5e971..77daf9eda59085 100644 --- a/src/cmd/compile/internal/noder/noder.go +++ b/src/cmd/compile/internal/noder/noder.go @@ -162,6 +162,7 @@ var allowedStdPragmas = map[string]bool{ "go:cgo_ldflag": true, "go:cgo_dynamic_linker": true, "go:embed": true, + "go:fix": true, "go:generate": true, } diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go index a4a701c9a2e54d..4202ff3358f51d 100644 --- a/src/cmd/compile/internal/reflectdata/reflect.go +++ b/src/cmd/compile/internal/reflectdata/reflect.go @@ -592,11 +592,21 @@ func TypePtrAt(pos src.XPos, t *types.Type) *ir.AddrExpr { // it may sometimes, but not always, be a type that can't implement the specified // interface. func ITabLsym(typ, iface *types.Type) *obj.LSym { + return itabLsym(typ, iface, true) +} + +func itabLsym(typ, iface *types.Type, allowNonImplement bool) *obj.LSym { s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString()) lsym := s.Linksym() + signatmu.Lock() + if lsym.Extra == nil { + ii := lsym.NewItabInfo() + ii.Type = typ + } + signatmu.Unlock() if !existed { - writeITab(lsym, typ, iface, true) + writeITab(lsym, typ, iface, allowNonImplement) } return lsym } @@ -605,13 +615,7 @@ func ITabLsym(typ, iface *types.Type) *obj.LSym { // *runtime.itab value for concrete type typ implementing interface // iface. func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr { - s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString()) - lsym := s.Linksym() - - if !existed { - writeITab(lsym, typ, iface, false) - } - + lsym := itabLsym(typ, iface, false) return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8]) } diff --git a/src/cmd/compile/internal/rttype/rttype.go b/src/cmd/compile/internal/rttype/rttype.go index a5aecb25356c65..aaf98dda152f1f 100644 --- a/src/cmd/compile/internal/rttype/rttype.go +++ b/src/cmd/compile/internal/rttype/rttype.go @@ -50,26 +50,26 @@ func Init() { // Note: this has to be called explicitly instead of being // an init function so it runs after the types package has // been properly initialized. - Type = fromReflect(reflect.TypeOf(abi.Type{})) - ArrayType = fromReflect(reflect.TypeOf(abi.ArrayType{})) - ChanType = fromReflect(reflect.TypeOf(abi.ChanType{})) - FuncType = fromReflect(reflect.TypeOf(abi.FuncType{})) - InterfaceType = fromReflect(reflect.TypeOf(abi.InterfaceType{})) - OldMapType = fromReflect(reflect.TypeOf(abi.OldMapType{})) - SwissMapType = fromReflect(reflect.TypeOf(abi.SwissMapType{})) - PtrType = fromReflect(reflect.TypeOf(abi.PtrType{})) - SliceType = fromReflect(reflect.TypeOf(abi.SliceType{})) - StructType = fromReflect(reflect.TypeOf(abi.StructType{})) + Type = FromReflect(reflect.TypeOf(abi.Type{})) + ArrayType = FromReflect(reflect.TypeOf(abi.ArrayType{})) + ChanType = FromReflect(reflect.TypeOf(abi.ChanType{})) + FuncType = FromReflect(reflect.TypeOf(abi.FuncType{})) + InterfaceType = FromReflect(reflect.TypeOf(abi.InterfaceType{})) + OldMapType = FromReflect(reflect.TypeOf(abi.OldMapType{})) + SwissMapType = FromReflect(reflect.TypeOf(abi.SwissMapType{})) + PtrType = FromReflect(reflect.TypeOf(abi.PtrType{})) + SliceType = FromReflect(reflect.TypeOf(abi.SliceType{})) + StructType = FromReflect(reflect.TypeOf(abi.StructType{})) - IMethod = fromReflect(reflect.TypeOf(abi.Imethod{})) - Method = fromReflect(reflect.TypeOf(abi.Method{})) - StructField = fromReflect(reflect.TypeOf(abi.StructField{})) - UncommonType = fromReflect(reflect.TypeOf(abi.UncommonType{})) + IMethod = FromReflect(reflect.TypeOf(abi.Imethod{})) + Method = FromReflect(reflect.TypeOf(abi.Method{})) + StructField = FromReflect(reflect.TypeOf(abi.StructField{})) + UncommonType = FromReflect(reflect.TypeOf(abi.UncommonType{})) - InterfaceSwitch = fromReflect(reflect.TypeOf(abi.InterfaceSwitch{})) - TypeAssert = fromReflect(reflect.TypeOf(abi.TypeAssert{})) + InterfaceSwitch = FromReflect(reflect.TypeOf(abi.InterfaceSwitch{})) + TypeAssert = FromReflect(reflect.TypeOf(abi.TypeAssert{})) - ITab = fromReflect(reflect.TypeOf(abi.ITab{})) + ITab = FromReflect(reflect.TypeOf(abi.ITab{})) // Make sure abi functions are correct. These functions are used // by the linker which doesn't have the ability to do type layout, @@ -92,8 +92,8 @@ func Init() { } } -// fromReflect translates from a host type to the equivalent target type. -func fromReflect(rt reflect.Type) *types.Type { +// FromReflect translates from a host type to the equivalent target type. +func FromReflect(rt reflect.Type) *types.Type { t := reflectToType(rt) types.CalcSize(t) return t @@ -108,6 +108,10 @@ func reflectToType(rt reflect.Type) *types.Type { return types.Types[types.TBOOL] case reflect.Int: return types.Types[types.TINT] + case reflect.Int8: + return types.Types[types.TINT8] + case reflect.Int16: + return types.Types[types.TINT16] case reflect.Int32: return types.Types[types.TINT32] case reflect.Uint8: @@ -116,9 +120,15 @@ func reflectToType(rt reflect.Type) *types.Type { return types.Types[types.TUINT16] case reflect.Uint32: return types.Types[types.TUINT32] + case reflect.Float32: + return types.Types[types.TFLOAT32] + case reflect.Float64: + return types.Types[types.TFLOAT64] case reflect.Uintptr: return types.Types[types.TUINTPTR] - case reflect.Ptr, reflect.Func, reflect.UnsafePointer: + case reflect.Ptr: + return types.NewPtr(reflectToType(rt.Elem())) + case reflect.Func, reflect.UnsafePointer: // TODO: there's no mechanism to distinguish different pointer types, // so we treat them all as unsafe.Pointer. return types.Types[types.TUNSAFEPTR] @@ -134,6 +144,12 @@ func reflectToType(rt reflect.Type) *types.Type { fields[i] = &types.Field{Sym: &types.Sym{Name: f.Name}, Type: ft} } return types.NewStruct(fields) + case reflect.Chan: + return types.NewChan(reflectToType(rt.Elem()), types.ChanDir(rt.ChanDir())) + case reflect.String: + return types.Types[types.TSTRING] + case reflect.Complex128: + return types.Types[types.TCOMPLEX128] default: base.Fatalf("unhandled kind %s", rt.Kind()) return nil @@ -155,7 +171,7 @@ func NewCursor(lsym *obj.LSym, off int64, t *types.Type) Cursor { // WritePtr writes a pointer "target" to the component at the location specified by c. func (c Cursor) WritePtr(target *obj.LSym) { - if c.typ.Kind() != types.TUNSAFEPTR { + if c.typ.Kind() != types.TUNSAFEPTR && c.typ.Kind() != types.TPTR { base.Fatalf("can't write ptr, it has kind %s", c.typ.Kind()) } if target == nil { diff --git a/src/cmd/compile/internal/ssa/_gen/386.rules b/src/cmd/compile/internal/ssa/_gen/386.rules index 67cfa3460aa3e1..216f5c2e2e79eb 100644 --- a/src/cmd/compile/internal/ssa/_gen/386.rules +++ b/src/cmd/compile/internal/ssa/_gen/386.rules @@ -940,3 +940,5 @@ (MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read8(sym, int64(off)))]) (MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) (MOVLload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVBLSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(int8(read8(sym, int64(off))))]) +(MOVWLSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64.rules b/src/cmd/compile/internal/ssa/_gen/AMD64.rules index ce9a6e99140a29..e590b41d5f5a2d 100644 --- a/src/cmd/compile/internal/ssa/_gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/_gen/AMD64.rules @@ -170,7 +170,7 @@ (Cvt32Fto64F ...) => (CVTSS2SD ...) (Cvt64Fto32F ...) => (CVTSD2SS ...) -(Round(32|64)F ...) => (Copy ...) +(Round(32|64)F ...) => (LoweredRound(32|64)F ...) // Floating-point min is tricky, as the hardware op isn't right for various special // cases (-0 and NaN). We use two hardware ops organized just right to make the @@ -404,11 +404,7 @@ (MOVQstoreconst [makeValAndOff(0,0)] destptr mem)) // Adjust zeros to be a multiple of 16 bytes. -(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE => - (Zero [s-s%16] (OffPtr destptr [s%16]) - (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) - -(Zero [s] destptr mem) && s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE => +(Zero [s] destptr mem) && s%16 != 0 && s > 16 && config.useSSE => (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) @@ -629,6 +625,23 @@ // x & 1 != 0 -> x & 1 (SETNE (TEST(B|W)const [1] x)) => (AND(L|L)const [1] x) (SETB (BT(L|Q)const [0] x)) => (AND(L|Q)const [1] x) +// x & 1 == 0 -> (x & 1) ^ 1 +(SETAE (BT(L|Q)const [0] x)) => (XORLconst [1] (ANDLconst [1] x)) + +// Shorten compare by rewriting x < 128 as x <= 127, which can be encoded in a single-byte immediate on x86. +(SETL c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETLE (CMP(Q|L)const [127] x)) +(SETB c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETBE (CMP(Q|L)const [127] x)) + +// x >= 128 -> x > 127 +(SETGE c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETG (CMP(Q|L)const [127] x)) +(SETAE c:(CMP(Q|L)const [128] x)) && c.Uses == 1 => (SETA (CMP(Q|L)const [127] x)) + +(CMOVQLT x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVQLE x y (CMP(Q|L)const [127] z)) +(CMOVLLT x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVLLE x y (CMP(Q|L)const [127] z)) +(LT c:(CMP(Q|L)const [128] z) yes no) && c.Uses == 1 => (LE (CMP(Q|L)const [127] z) yes no) +(CMOVQGE x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVQGT x y (CMP(Q|L)const [127] z)) +(CMOVLGE x y c:(CMP(Q|L)const [128] z)) && c.Uses == 1 => (CMOVLGT x y (CMP(Q|L)const [127] z)) +(GE c:(CMP(Q|L)const [128] z) yes no) && c.Uses == 1 => (GT (CMP(Q|L)const [127] z) yes no) // Recognize bit tests: a&(1<>b)&1 != 0 by building the above rules // and further combining shifts. (BT(Q|L)const [c] (SHRQconst [d] x)) && (c+d)<64 => (BTQconst [c+d] x) +(BT(Q|L)const [c] (ADDQ x x)) && c>1 => (BT(Q|L)const [c-1] x) (BT(Q|L)const [c] (SHLQconst [d] x)) && c>d => (BT(Q|L)const [c-d] x) (BT(Q|L)const [0] s:(SHRQ x y)) => (BTQ y x) (BTLconst [c] (SHRLconst [d] x)) && (c+d)<32 => (BTLconst [c+d] x) +(BTLconst [c] (ADDL x x)) && c>1 => (BTLconst [c-1] x) (BTLconst [c] (SHLLconst [d] x)) && c>d => (BTLconst [c-d] x) (BTLconst [0] s:(SHR(L|XL) x y)) => (BTL y x) @@ -704,11 +719,11 @@ // We thus special-case them, by detecting the shift patterns. // Special case resetting first/last bit -(SHL(L|Q)const [1] (SHR(L|Q)const [1] x)) +(ADD(L|Q) (SHR(L|Q)const [1] x) (SHR(L|Q)const [1] x)) => (AND(L|Q)const [-2] x) -(SHRLconst [1] (SHLLconst [1] x)) +(SHRLconst [1] (ADDL x x)) => (ANDLconst [0x7fffffff] x) -(SHRQconst [1] (SHLQconst [1] x)) +(SHRQconst [1] (ADDQ x x)) => (BTRQconst [63] x) // Special case testing first/last bit (with double-shift generated by generic.rules) @@ -935,17 +950,19 @@ (MUL(Q|L)const [c] x) && c%5 == 0 && isPowerOfTwo(c/5) => (SHL(Q|L)const [int8(log32(c/5))] (LEA(Q|L)4 x x)) (MUL(Q|L)const [c] x) && c%9 == 0 && isPowerOfTwo(c/9) => (SHL(Q|L)const [int8(log32(c/9))] (LEA(Q|L)8 x x)) +// Prefer addition when shifting left by one +(SHL(Q|L)const [1] x) => (ADD(Q|L) x x) + // combine add/shift into LEAQ/LEAL (ADD(L|Q) x (SHL(L|Q)const [3] y)) => (LEA(L|Q)8 x y) (ADD(L|Q) x (SHL(L|Q)const [2] y)) => (LEA(L|Q)4 x y) -(ADD(L|Q) x (SHL(L|Q)const [1] y)) => (LEA(L|Q)2 x y) (ADD(L|Q) x (ADD(L|Q) y y)) => (LEA(L|Q)2 x y) (ADD(L|Q) x (ADD(L|Q) x y)) => (LEA(L|Q)2 y x) // combine ADDQ/ADDQconst into LEAQ1/LEAL1 (ADD(Q|L)const [c] (ADD(Q|L) x y)) => (LEA(Q|L)1 [c] x y) (ADD(Q|L) (ADD(Q|L)const [c] x) y) => (LEA(Q|L)1 [c] x y) -(ADD(Q|L)const [c] (SHL(Q|L)const [1] x)) => (LEA(Q|L)1 [c] x x) +(ADD(Q|L)const [c] (ADD(Q|L) x x)) => (LEA(Q|L)1 [c] x x) // fold ADDQ/ADDL into LEAQ/LEAL (ADD(Q|L)const [c] (LEA(Q|L) [d] {s} x)) && is32Bit(int64(c)+int64(d)) => (LEA(Q|L) [c+d] {s} x) @@ -967,12 +984,18 @@ (LEA(Q|L)8 [c] {s} x (ADD(Q|L)const [d] y)) && is32Bit(int64(c)+8*int64(d)) && y.Op != OpSB => (LEA(Q|L)8 [c+8*d] {s} x y) // fold shifts into LEAQx/LEALx -(LEA(Q|L)1 [c] {s} x (SHL(Q|L)const [1] y)) => (LEA(Q|L)2 [c] {s} x y) +(LEA(Q|L)1 [c] {s} x (ADD(Q|L) y y)) => (LEA(Q|L)2 [c] {s} x y) (LEA(Q|L)1 [c] {s} x (SHL(Q|L)const [2] y)) => (LEA(Q|L)4 [c] {s} x y) (LEA(Q|L)1 [c] {s} x (SHL(Q|L)const [3] y)) => (LEA(Q|L)8 [c] {s} x y) -(LEA(Q|L)2 [c] {s} x (SHL(Q|L)const [1] y)) => (LEA(Q|L)4 [c] {s} x y) +(LEA(Q|L)2 [c] {s} x (ADD(Q|L) y y)) => (LEA(Q|L)4 [c] {s} x y) (LEA(Q|L)2 [c] {s} x (SHL(Q|L)const [2] y)) => (LEA(Q|L)8 [c] {s} x y) -(LEA(Q|L)4 [c] {s} x (SHL(Q|L)const [1] y)) => (LEA(Q|L)8 [c] {s} x y) +(LEA(Q|L)4 [c] {s} x (ADD(Q|L) y y)) => (LEA(Q|L)8 [c] {s} x y) + +// (x + x) << 1 -> x << 2 +(LEA(Q|L)2 [0] {s} (ADD(Q|L) x x) x) && s == nil => (SHL(Q|L)const [2] x) + +// (x + x) << 2 -> x << 3 and similar +(SHL(Q|L)const [c] (ADD(Q|L) x x)) => (SHL(Q|L)const [c+1] x) // reverse ordering of compare instruction (SETL (InvertFlags x)) => (SETG x) @@ -1581,6 +1604,9 @@ (MULSDload x [off] {sym} ptr (MOVQstore [off] {sym} ptr y _)) => (MULSD x (MOVQi2f y)) (MULSSload x [off] {sym} ptr (MOVLstore [off] {sym} ptr y _)) => (MULSS x (MOVLi2f y)) +// Detect FMA +(ADDS(S|D) (MULS(S|D) x y) z) && buildcfg.GOAMD64 >= 3 && z.Block.Func.useFMA(v) => (VFMADD231S(S|D) z x y) + // Redirect stores to use the other register set. (MOVQstore [off] {sym} ptr (MOVQf2i val) mem) => (MOVSDstore [off] {sym} ptr val mem) (MOVLstore [off] {sym} ptr (MOVLf2i val) mem) => (MOVSSstore [off] {sym} ptr val mem) @@ -1640,8 +1666,13 @@ (MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read8(sym, int64(off)))]) (MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) -(MOVLload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVLload [off] {sym} (SB) _) && symIsRO(sym) => (MOVLconst [int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) (MOVQload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVBQSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(int8(read8(sym, int64(off))))]) +(MOVWQSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) +(MOVLQSXload [off] {sym} (SB) _) && symIsRO(sym) => (MOVQconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) + + (MOVOstore [dstOff] {dstSym} ptr (MOVOload [srcOff] {srcSym} (SB) _) mem) && symIsRO(srcSym) => (MOVQstore [dstOff+8] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff)+8, config.ctxt.Arch.ByteOrder))]) (MOVQstore [dstOff] {dstSym} ptr (MOVQconst [int64(read64(srcSym, int64(srcOff), config.ctxt.Arch.ByteOrder))]) mem)) diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go index 23fb2361b56198..1cce32eba32523 100644 --- a/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go +++ b/src/cmd/compile/internal/ssa/_gen/AMD64Ops.go @@ -692,9 +692,15 @@ func init() { // ROUNDSD instruction is only guaraneteed to be available if GOAMD64>=v2. // For GOAMD64=v3. + // x==S for float32, x==D for float64 + // arg0 + arg1*arg2, with no intermediate rounding. + {name: "VFMADD231SS", argLength: 3, reg: fp31, resultInArg0: true, asm: "VFMADD231SS"}, {name: "VFMADD231SD", argLength: 3, reg: fp31, resultInArg0: true, asm: "VFMADD231SD"}, // Note that these operations don't exactly match the semantics of Go's @@ -758,7 +764,7 @@ func init() { {name: "MOVLQSX", argLength: 1, reg: gp11, asm: "MOVLQSX"}, // sign extend arg0 from int32 to int64 {name: "MOVLQZX", argLength: 1, reg: gp11, asm: "MOVL"}, // zero extend arg0 from int32 to int64 - {name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint + {name: "MOVLconst", reg: gp01, asm: "MOVL", typ: "UInt32", aux: "Int32", rematerializeable: true}, // 32 low bits of auxint (upper 32 are zeroed) {name: "MOVQconst", reg: gp01, asm: "MOVQ", typ: "UInt64", aux: "Int64", rematerializeable: true}, // auxint {name: "CVTTSD2SL", argLength: 1, reg: fpgp, asm: "CVTTSD2SL"}, // convert float64 to int32 @@ -1156,7 +1162,7 @@ func init() { // // output[i] = input. {name: "PSHUFBbroadcast", argLength: 1, reg: fp11, resultInArg0: true, asm: "PSHUFB"}, // PSHUFB with mask zero, (GOAMD64=v1) - {name: "VPBROADCASTB", argLength: 1, reg: gpfp, asm: "VPBROADCASTB"}, // Broadcast input byte from gp (GOAMD64=v3) + {name: "VPBROADCASTB", argLength: 1, reg: gpfp, asm: "VPBROADCASTB"}, // Broadcast input byte from gp (GOAMD64=v3) // Byte negate/zero/preserve (GOAMD64=v2). // @@ -1180,7 +1186,7 @@ func init() { // } else { // output[i] = 0 // } - {name: "PCMPEQB", argLength: 2, reg: fp21, resultInArg0: true, asm: "PCMPEQB"}, + {name: "PCMPEQB", argLength: 2, reg: fp21, resultInArg0: true, asm: "PCMPEQB", commutative: true}, // Byte sign mask. Output is a bitmap of sign bits from each input byte. // diff --git a/src/cmd/compile/internal/ssa/_gen/ARM.rules b/src/cmd/compile/internal/ssa/_gen/ARM.rules index 9cdb5d8ad541c9..a3bb2c312f9c16 100644 --- a/src/cmd/compile/internal/ssa/_gen/ARM.rules +++ b/src/cmd/compile/internal/ssa/_gen/ARM.rules @@ -1473,3 +1473,5 @@ (MOVBUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(read8(sym, int64(off)))]) (MOVHUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) (MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(int8(read8(sym, int64(off))))]) +(MOVHload [off] {sym} (SB) _) && symIsRO(sym) => (MOVWconst [int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64.rules b/src/cmd/compile/internal/ssa/_gen/ARM64.rules index 6652d2ec014c26..3696e17d9ce957 100644 --- a/src/cmd/compile/internal/ssa/_gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/_gen/ARM64.rules @@ -1940,6 +1940,9 @@ (MOVHUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) (MOVWUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) (MOVDload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(int8(read8(sym, int64(off))))]) +(MOVHload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) +(MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVDconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) // Prefetch instructions (aux is option: 0 - PLDL1KEEP; 1 - PLDL1STRM) (PrefetchCache addr mem) => (PRFM [0] addr mem) diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go b/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go index c9cb62cd17cee2..53e6dbec3f159f 100644 --- a/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go +++ b/src/cmd/compile/internal/ssa/_gen/ARM64Ops.go @@ -169,9 +169,11 @@ func init() { fp2flags = regInfo{inputs: []regMask{fp, fp}} fp1flags = regInfo{inputs: []regMask{fp}} fpload = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}} + fpload2 = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp, fp}} fp2load = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{fp}} fpstore = regInfo{inputs: []regMask{gpspsbg, fp}} - fpstore2 = regInfo{inputs: []regMask{gpspsbg, gpg, fp}} + fpstoreidx = regInfo{inputs: []regMask{gpspsbg, gpg, fp}} + fpstore2 = regInfo{inputs: []regMask{gpspsbg, fp, fp}} readflags = regInfo{inputs: nil, outputs: []regMask{gp}} prefreg = regInfo{inputs: []regMask{gpspsbg}} ) @@ -369,16 +371,27 @@ func init() { {name: "MOVDaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVD", rematerializeable: true, symEffect: "Addr"}, // arg0 + auxInt + aux.(*gc.Sym), arg0=SP/SB - {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "LDP", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDP", typ: "(UInt64,UInt64)", faultOnNilArg0: true, symEffect: "Read"}, // load from ptr = arg0 + auxInt + aux, returns the tuple <*(*uint64)ptr, *(*uint64)(ptr+8)>. arg1=mem. - {name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. - {name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "Int32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVWUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVWU", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "MOVDload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVD", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "FMOVSload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVS", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + {name: "FMOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "FMOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"}, // load from arg0 + auxInt + aux. arg1=mem. + + // LDP instructions load the contents of two adjacent locations in memory into registers. + // Address to start loading is addr = arg0 + auxInt + aux. + // x := *(*T)(addr) + // y := *(*T)(addr+sizeof(T)) + // arg1=mem + // Returns the tuple . + {name: "LDP", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDP", typ: "(UInt64,UInt64)", faultOnNilArg0: true, symEffect: "Read"}, // T=int64 (gp reg destination) + {name: "LDPW", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDPW", typ: "(UInt32,UInt32)", faultOnNilArg0: true, symEffect: "Read"}, // T=int32 (gp reg destination) unsigned extension + {name: "LDPSW", argLength: 2, reg: gpload2, aux: "SymOff", asm: "LDPSW", typ: "(Int32,Int32)", faultOnNilArg0: true, symEffect: "Read"}, // T=int32 (gp reg destination) signed extension + {name: "FLDPD", argLength: 2, reg: fpload2, aux: "SymOff", asm: "FLDPD", typ: "(Float64,Float64)", faultOnNilArg0: true, symEffect: "Read"}, // T=float64 (fp reg destination) + {name: "FLDPS", argLength: 2, reg: fpload2, aux: "SymOff", asm: "FLDPS", typ: "(Float32,Float32)", faultOnNilArg0: true, symEffect: "Read"}, // T=float32 (fp reg destination) // register indexed load {name: "MOVDloadidx", argLength: 3, reg: gp2load, asm: "MOVD", typ: "UInt64"}, // load 64-bit dword from arg0 + arg1, arg2 = mem. @@ -404,24 +417,33 @@ func init() { {name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. {name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. {name: "MOVDstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. - {name: "STP", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 16 bytes of arg1 and arg2 to arg0 + auxInt + aux. arg3=mem. {name: "FMOVSstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 4 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. {name: "FMOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "FMOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 8 bytes of arg1 to arg0 + auxInt + aux. arg2=mem. + // STP instructions store the contents of two registers to adjacent locations in memory. + // Address to start storing is addr = arg0 + auxInt + aux. + // *(*T)(addr) = arg1 + // *(*T)(addr+sizeof(T)) = arg2 + // arg3=mem. Returns mem. + {name: "STP", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STP", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=int64 (gp reg source) + {name: "STPW", argLength: 4, reg: gpstore2, aux: "SymOff", asm: "STPW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=int32 (gp reg source) + {name: "FSTPD", argLength: 4, reg: fpstore2, aux: "SymOff", asm: "FSTPD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=float64 (fp reg source) + {name: "FSTPS", argLength: 4, reg: fpstore2, aux: "SymOff", asm: "FSTPS", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // T=float32 (fp reg source) + // register indexed store - {name: "MOVBstoreidx", argLength: 4, reg: gpstore2, asm: "MOVB", typ: "Mem"}, // store 1 byte of arg2 to arg0 + arg1, arg3 = mem. - {name: "MOVHstoreidx", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1, arg3 = mem. - {name: "MOVWstoreidx", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1, arg3 = mem. - {name: "MOVDstoreidx", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1, arg3 = mem. - {name: "FMOVSstoreidx", argLength: 4, reg: fpstore2, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1, arg3=mem. - {name: "FMOVDstoreidx", argLength: 4, reg: fpstore2, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1, arg3=mem. + {name: "MOVBstoreidx", argLength: 4, reg: gpstore2, asm: "MOVB", typ: "Mem"}, // store 1 byte of arg2 to arg0 + arg1, arg3 = mem. + {name: "MOVHstoreidx", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1, arg3 = mem. + {name: "MOVWstoreidx", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1, arg3 = mem. + {name: "MOVDstoreidx", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1, arg3 = mem. + {name: "FMOVSstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1, arg3=mem. + {name: "FMOVDstoreidx", argLength: 4, reg: fpstoreidx, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1, arg3=mem. // shifted register indexed store - {name: "MOVHstoreidx2", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1*2, arg3 = mem. - {name: "MOVWstoreidx4", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1*4, arg3 = mem. - {name: "MOVDstoreidx8", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1*8, arg3 = mem. - {name: "FMOVSstoreidx4", argLength: 4, reg: fpstore2, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1*4, arg3=mem. - {name: "FMOVDstoreidx8", argLength: 4, reg: fpstore2, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1*8, arg3=mem. + {name: "MOVHstoreidx2", argLength: 4, reg: gpstore2, asm: "MOVH", typ: "Mem"}, // store 2 bytes of arg2 to arg0 + arg1*2, arg3 = mem. + {name: "MOVWstoreidx4", argLength: 4, reg: gpstore2, asm: "MOVW", typ: "Mem"}, // store 4 bytes of arg2 to arg0 + arg1*4, arg3 = mem. + {name: "MOVDstoreidx8", argLength: 4, reg: gpstore2, asm: "MOVD", typ: "Mem"}, // store 8 bytes of arg2 to arg0 + arg1*8, arg3 = mem. + {name: "FMOVSstoreidx4", argLength: 4, reg: fpstoreidx, asm: "FMOVS", typ: "Mem"}, // store 32-bit float of arg2 to arg0 + arg1*4, arg3=mem. + {name: "FMOVDstoreidx8", argLength: 4, reg: fpstoreidx, asm: "FMOVD", typ: "Mem"}, // store 64-bit float of arg2 to arg0 + arg1*8, arg3=mem. {name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 1 byte of zero to arg0 + auxInt + aux. arg1=mem. {name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // store 2 bytes of zero to arg0 + auxInt + aux. arg1=mem. diff --git a/src/cmd/compile/internal/ssa/_gen/MIPS64.rules b/src/cmd/compile/internal/ssa/_gen/MIPS64.rules index 8aed350039ab7e..cc3985ecdd0a6f 100644 --- a/src/cmd/compile/internal/ssa/_gen/MIPS64.rules +++ b/src/cmd/compile/internal/ssa/_gen/MIPS64.rules @@ -811,7 +811,10 @@ (SGTU x x) => (MOVVconst [0]) // fold readonly sym load -(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(read8(sym, int64(off)))]) -(MOVHload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) -(MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVBUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(read8(sym, int64(off)))]) +(MOVHUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVWUload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) (MOVVload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(read64(sym, int64(off), config.ctxt.Arch.ByteOrder))]) +(MOVBload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(int8(read8(sym, int64(off))))]) +(MOVHload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) +(MOVWload [off] {sym} (SB) _) && symIsRO(sym) => (MOVVconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) diff --git a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules index 9ae96043810cfd..a69df619a576c6 100644 --- a/src/cmd/compile/internal/ssa/_gen/RISCV64.rules +++ b/src/cmd/compile/internal/ssa/_gen/RISCV64.rules @@ -270,65 +270,29 @@ // We need to fold MOVaddr into the LD/MOVDstore ops so that the live variable analysis // knows what variables are being read/written by the ops. -(MOVBUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVBload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVHUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVHUload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVHload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVHload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVWUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVWUload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVWload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem) -(MOVDload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVDload [off1+off2] {mergeSym(sym1,sym2)} base mem) - -(MOVBstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOVHstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOVWstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOVDstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) && is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) => - (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) -(MOVBstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVHstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVWstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) -(MOVDstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) && canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) => - (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) - -(MOVBUload [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => - (MOVBUload [off1+int32(off2)] {sym} base mem) -(MOVBload [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => - (MOVBload [off1+int32(off2)] {sym} base mem) -(MOVHUload [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => - (MOVHUload [off1+int32(off2)] {sym} base mem) -(MOVHload [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => - (MOVHload [off1+int32(off2)] {sym} base mem) -(MOVWUload [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => - (MOVWUload [off1+int32(off2)] {sym} base mem) -(MOVWload [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => - (MOVWload [off1+int32(off2)] {sym} base mem) -(MOVDload [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => - (MOVDload [off1+int32(off2)] {sym} base mem) - -(MOVBstore [off1] {sym} (ADDI [off2] base) val mem) && is32Bit(int64(off1)+off2) => - (MOVBstore [off1+int32(off2)] {sym} base val mem) -(MOVHstore [off1] {sym} (ADDI [off2] base) val mem) && is32Bit(int64(off1)+off2) => - (MOVHstore [off1+int32(off2)] {sym} base val mem) -(MOVWstore [off1] {sym} (ADDI [off2] base) val mem) && is32Bit(int64(off1)+off2) => - (MOVWstore [off1+int32(off2)] {sym} base val mem) -(MOVDstore [off1] {sym} (ADDI [off2] base) val mem) && is32Bit(int64(off1)+off2) => - (MOVDstore [off1+int32(off2)] {sym} base val mem) -(MOVBstorezero [off1] {sym} (ADDI [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVBstorezero [off1+int32(off2)] {sym} ptr mem) -(MOVHstorezero [off1] {sym} (ADDI [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVHstorezero [off1+int32(off2)] {sym} ptr mem) -(MOVWstorezero [off1] {sym} (ADDI [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVWstorezero [off1+int32(off2)] {sym} ptr mem) -(MOVDstorezero [off1] {sym} (ADDI [off2] ptr) mem) && is32Bit(int64(off1)+off2) => (MOVDstorezero [off1+int32(off2)] {sym} ptr mem) +(MOV(B|BU|H|HU|W|WU|D)load [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && + is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && + (base.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|BU|H|HU|W|WU|D)load [off1+off2] {mergeSym(sym1,sym2)} base mem) + +(MOV(B|H|W|D)store [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) && + is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && + (base.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|H|W|D)store [off1+off2] {mergeSym(sym1,sym2)} base val mem) + +(MOV(B|H|W|D)storezero [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) && + canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && + (base.Op != OpSB || !config.ctxt.Flag_dynlink) => + (MOV(B|H|W|D)storezero [off1+off2] {mergeSym(sym1,sym2)} base mem) + +(MOV(B|BU|H|HU|W|WU|D)load [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => + (MOV(B|BU|H|HU|W|WU|D)load [off1+int32(off2)] {sym} base mem) + +(MOV(B|H|W|D)store [off1] {sym} (ADDI [off2] base) val mem) && is32Bit(int64(off1)+off2) => + (MOV(B|H|W|D)store [off1+int32(off2)] {sym} base val mem) + +(MOV(B|H|W|D)storezero [off1] {sym} (ADDI [off2] base) mem) && is32Bit(int64(off1)+off2) => + (MOV(B|H|W|D)storezero [off1+int32(off2)] {sym} base mem) // Similarly, fold ADDI into MOVaddr to avoid confusing live variable analysis // with OffPtr -> ADDI. diff --git a/src/cmd/compile/internal/ssa/_gen/Wasm.rules b/src/cmd/compile/internal/ssa/_gen/Wasm.rules index 91a9fc5e4a9772..08cadabe0ea4c2 100644 --- a/src/cmd/compile/internal/ssa/_gen/Wasm.rules +++ b/src/cmd/compile/internal/ssa/_gen/Wasm.rules @@ -395,3 +395,6 @@ (I64Load32U [off] (LoweredAddr {sym} [off2] (SB)) _) && symIsRO(sym) && isU32Bit(off+int64(off2)) => (I64Const [int64(read32(sym, off+int64(off2), config.ctxt.Arch.ByteOrder))]) (I64Load16U [off] (LoweredAddr {sym} [off2] (SB)) _) && symIsRO(sym) && isU32Bit(off+int64(off2)) => (I64Const [int64(read16(sym, off+int64(off2), config.ctxt.Arch.ByteOrder))]) (I64Load8U [off] (LoweredAddr {sym} [off2] (SB)) _) && symIsRO(sym) && isU32Bit(off+int64(off2)) => (I64Const [int64(read8(sym, off+int64(off2)))]) +(I64Load32S [off] (LoweredAddr {sym} [off2] (SB)) _) && symIsRO(sym) && isU32Bit(off+int64(off2)) => (I64Const [int64(int32(read32(sym, off+int64(off2), config.ctxt.Arch.ByteOrder)))]) +(I64Load16S [off] (LoweredAddr {sym} [off2] (SB)) _) && symIsRO(sym) && isU32Bit(off+int64(off2)) => (I64Const [int64(int16(read16(sym, off+int64(off2), config.ctxt.Arch.ByteOrder)))]) +(I64Load8S [off] (LoweredAddr {sym} [off2] (SB)) _) && symIsRO(sym) && isU32Bit(off+int64(off2)) => (I64Const [int64(int8(read8(sym, off+int64(off2))))]) diff --git a/src/cmd/compile/internal/ssa/_gen/generic.rules b/src/cmd/compile/internal/ssa/_gen/generic.rules index 9e2e8772c154ab..0339370517ca83 100644 --- a/src/cmd/compile/internal/ssa/_gen/generic.rules +++ b/src/cmd/compile/internal/ssa/_gen/generic.rules @@ -353,6 +353,10 @@ (Add64 (Const64 [c*d]) (Mul64 (Const64 [c]) x)) (Mul32 (Const32 [c]) (Add32 (Const32 [d]) x)) => (Add32 (Const32 [c*d]) (Mul32 (Const32 [c]) x)) +(Mul16 (Const16 [c]) (Add16 (Const16 [d]) x)) => + (Add16 (Const16 [c*d]) (Mul16 (Const16 [c]) x)) +(Mul8 (Const8 [c]) (Add8 (Const8 [d]) x)) => + (Add8 (Const8 [c*d]) (Mul8 (Const8 [c]) x)) // Rewrite x*y ± x*z to x*(y±z) (Add(64|32|16|8) (Mul(64|32|16|8) x y) (Mul(64|32|16|8) x z)) @@ -2068,6 +2072,11 @@ (NilCheck ptr:(Addr {_} (SB)) _) => ptr (NilCheck ptr:(Convert (Addr {_} (SB)) _) _) => ptr +// Addresses of locals are always non-nil. +(NilCheck ptr:(LocalAddr _ _) _) + && warnRule(fe.Debug_checknil(), v, "removed nil check") + => ptr + // Nil checks of nil checks are redundant. // See comment at the end of https://go-review.googlesource.com/c/go/+/537775. (NilCheck ptr:(NilCheck _ _) _ ) => ptr @@ -2770,3 +2779,38 @@ // If we don't use the result of cmpstring, might as well not call it. // Note that this could pretty easily generalize to any pure function. (SelectN [1] c:(StaticLECall {f} _ _ mem)) && c.Uses == 1 && isSameCall(f, "runtime.cmpstring") && clobber(c) => mem + +// We can easily compute the result of efaceeq if +// we know the underlying type is pointer-ish. +(StaticLECall {f} typ_ x y mem) + && isSameCall(f, "runtime.efaceeq") + && isDirectType(typ_) + && clobber(v) + => (MakeResult (EqPtr x y) mem) + +// We can easily compute the result of ifaceeq if +// we know the underlying type is pointer-ish. +(StaticLECall {f} itab x y mem) + && isSameCall(f, "runtime.ifaceeq") + && isDirectIface(itab) + && clobber(v) + => (MakeResult (EqPtr x y) mem) + +// If we use the result of slicebytetostring in a map lookup operation, +// then we don't need to actually do the []byte->string conversion. +// We can just use the ptr/len of the byte slice directly as a (temporary) string. +// +// Note that this does not handle some obscure cases like +// m[[2]string{string(b1), string(b2)}]. There is code in ../walk/order.go +// which handles some of those cases. +(StaticLECall {f} [argsize] typ_ map_ key:(SelectN [0] sbts:(StaticLECall {g} _ ptr len mem)) m:(SelectN [1] sbts)) + && (isSameCall(f, "runtime.mapaccess1_faststr") + || isSameCall(f, "runtime.mapaccess2_faststr") + || isSameCall(f, "runtime.mapdelete_faststr")) + && isSameCall(g, "runtime.slicebytetostring") + && key.Uses == 1 + && sbts.Uses == 2 + && resetCopy(m, mem) + && clobber(sbts) + && clobber(key) +=> (StaticLECall {f} [argsize] typ_ map_ (StringMake ptr len) mem) diff --git a/src/cmd/compile/internal/ssa/_gen/rulegen.go b/src/cmd/compile/internal/ssa/_gen/rulegen.go index b6356315014411..4374d3e153f69e 100644 --- a/src/cmd/compile/internal/ssa/_gen/rulegen.go +++ b/src/cmd/compile/internal/ssa/_gen/rulegen.go @@ -5,7 +5,8 @@ // This program generates Go code that applies rewrite rules to a Value. // The generated code implements a function of type func (v *Value) bool // which reports whether if did something. -// Ideas stolen from Swift: http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-2000-2.html +// Ideas stolen from the Swift Java compiler: +// https://bitsavers.org/pdf/dec/tech_reports/WRL-2000-2.pdf package main diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go index 3f46599a3e5756..634a6f68642e71 100644 --- a/src/cmd/compile/internal/ssa/compile.go +++ b/src/cmd/compile/internal/ssa/compile.go @@ -488,6 +488,7 @@ var passes = [...]pass{ {name: "lower", fn: lower, required: true}, {name: "addressing modes", fn: addressingModes, required: false}, {name: "late lower", fn: lateLower, required: true}, + {name: "pair", fn: pair}, {name: "lowered deadcode for cse", fn: deadcode}, // deadcode immediately before CSE avoids CSE making dead values live again {name: "lowered cse", fn: cse}, {name: "elim unread autos", fn: elimUnreadAutos}, diff --git a/src/cmd/compile/internal/ssa/deadstore.go b/src/cmd/compile/internal/ssa/deadstore.go index 9f61ef29b9d527..29cf1e91e0f1a6 100644 --- a/src/cmd/compile/internal/ssa/deadstore.go +++ b/src/cmd/compile/internal/ssa/deadstore.go @@ -52,9 +52,8 @@ func dse(f *Func) { if v.Op == OpLocalAddr { if _, ok := localAddrs[v.Aux]; !ok { localAddrs[v.Aux] = v - } else { - continue } + continue } if v.Op == OpInlMark { // Not really a use of the memory. See #67957. diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go index df1ddfa69edfc1..718f4c93821dcf 100644 --- a/src/cmd/compile/internal/ssa/opGen.go +++ b/src/cmd/compile/internal/ssa/opGen.go @@ -924,6 +924,9 @@ const ( OpAMD64SQRTSD OpAMD64SQRTSS OpAMD64ROUNDSD + OpAMD64LoweredRound32F + OpAMD64LoweredRound64F + OpAMD64VFMADD231SS OpAMD64VFMADD231SD OpAMD64MINSD OpAMD64MINSS @@ -1600,9 +1603,13 @@ const ( OpARM64MOVWload OpARM64MOVWUload OpARM64MOVDload - OpARM64LDP OpARM64FMOVSload OpARM64FMOVDload + OpARM64LDP + OpARM64LDPW + OpARM64LDPSW + OpARM64FLDPD + OpARM64FLDPS OpARM64MOVDloadidx OpARM64MOVWloadidx OpARM64MOVWUloadidx @@ -1623,9 +1630,12 @@ const ( OpARM64MOVHstore OpARM64MOVWstore OpARM64MOVDstore - OpARM64STP OpARM64FMOVSstore OpARM64FMOVDstore + OpARM64STP + OpARM64STPW + OpARM64FSTPD + OpARM64FSTPS OpARM64MOVBstoreidx OpARM64MOVHstoreidx OpARM64MOVWstoreidx @@ -12060,6 +12070,50 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "LoweredRound32F", + argLen: 1, + resultInArg0: true, + zeroWidth: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + outputs: []outputInfo{ + {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + }, + }, + { + name: "LoweredRound64F", + argLen: 1, + resultInArg0: true, + zeroWidth: true, + reg: regInfo{ + inputs: []inputInfo{ + {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + outputs: []outputInfo{ + {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + }, + }, + { + name: "VFMADD231SS", + argLen: 3, + resultInArg0: true, + asm: x86.AVFMADD231SS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + {1, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + {2, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + outputs: []outputInfo{ + {0, 2147418112}, // X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 + }, + }, + }, { name: "VFMADD231SD", argLen: 3, @@ -15414,6 +15468,7 @@ var opcodeTable = [...]opInfo{ { name: "PCMPEQB", argLen: 2, + commutative: true, resultInArg0: true, asm: x86.APCMPEQB, reg: regInfo{ @@ -21544,6 +21599,38 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "FMOVSload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AFMOVS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FMOVDload", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AFMOVD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, { name: "LDP", auxType: auxSymOff, @@ -21562,34 +21649,70 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FMOVSload", + name: "LDPW", auxType: auxSymOff, argLen: 2, faultOnNilArg0: true, symEffect: SymRead, - asm: arm64.AFMOVS, + asm: arm64.ALDPW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + }, + }, + { + name: "LDPSW", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.ALDPSW, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + outputs: []outputInfo{ + {0, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + }, + }, + }, + { + name: "FLDPD", + auxType: auxSymOff, + argLen: 2, + faultOnNilArg0: true, + symEffect: SymRead, + asm: arm64.AFLDPD, reg: regInfo{ inputs: []inputInfo{ {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "FMOVDload", + name: "FLDPS", auxType: auxSymOff, argLen: 2, faultOnNilArg0: true, symEffect: SymRead, - asm: arm64.AFMOVD, + asm: arm64.AFLDPS, reg: regInfo{ inputs: []inputInfo{ {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB }, outputs: []outputInfo{ {0, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, @@ -21873,6 +21996,34 @@ var opcodeTable = [...]opInfo{ }, }, }, + { + name: "FMOVSstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AFMOVS, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, + { + name: "FMOVDstore", + auxType: auxSymOff, + argLen: 3, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AFMOVD, + reg: regInfo{ + inputs: []inputInfo{ + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + }, + }, + }, { name: "STP", auxType: auxSymOff, @@ -21889,30 +22040,47 @@ var opcodeTable = [...]opInfo{ }, }, { - name: "FMOVSstore", + name: "STPW", auxType: auxSymOff, - argLen: 3, + argLen: 4, faultOnNilArg0: true, symEffect: SymWrite, - asm: arm64.AFMOVS, + asm: arm64.ASTPW, + reg: regInfo{ + inputs: []inputInfo{ + {1, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {2, 805044223}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 + {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB + }, + }, + }, + { + name: "FSTPD", + auxType: auxSymOff, + argLen: 4, + faultOnNilArg0: true, + symEffect: SymWrite, + asm: arm64.AFSTPD, reg: regInfo{ inputs: []inputInfo{ {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, { - name: "FMOVDstore", + name: "FSTPS", auxType: auxSymOff, - argLen: 3, + argLen: 4, faultOnNilArg0: true, symEffect: SymWrite, - asm: arm64.AFMOVD, + asm: arm64.AFSTPS, reg: regInfo{ inputs: []inputInfo{ {0, 9223372038733561855}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R19 R20 R21 R22 R23 R24 R25 R26 g R30 SP SB {1, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 + {2, 9223372034707292160}, // F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 }, }, }, diff --git a/src/cmd/compile/internal/ssa/pair.go b/src/cmd/compile/internal/ssa/pair.go new file mode 100644 index 00000000000000..8e022df036e5b2 --- /dev/null +++ b/src/cmd/compile/internal/ssa/pair.go @@ -0,0 +1,360 @@ +// Copyright 2016 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. + +package ssa + +import ( + "cmd/compile/internal/ir" + "cmd/compile/internal/types" + "slices" +) + +// The pair pass finds memory operations that can be paired up +// into single 2-register memory instructions. +func pair(f *Func) { + // Only arm64 for now. This pass is fairly arch-specific. + switch f.Config.arch { + case "arm64": + default: + return + } + pairLoads(f) + pairStores(f) +} + +type pairableLoadInfo struct { + width int64 // width of one element in the pair, in bytes + pair Op +} + +// All pairableLoad ops must take 2 arguments, a pointer and a memory. +// They must also take an offset in Aux/AuxInt. +var pairableLoads = map[Op]pairableLoadInfo{ + OpARM64MOVDload: {8, OpARM64LDP}, + OpARM64MOVWUload: {4, OpARM64LDPW}, + OpARM64MOVWload: {4, OpARM64LDPSW}, + // TODO: conceivably we could pair a signed and unsigned load + // if we knew the upper bits of one of them weren't being used. + OpARM64FMOVDload: {8, OpARM64FLDPD}, + OpARM64FMOVSload: {4, OpARM64FLDPS}, +} + +type pairableStoreInfo struct { + width int64 // width of one element in the pair, in bytes + pair Op +} + +// All pairableStore keys must take 3 arguments, a pointer, a value, and a memory. +// All pairableStore values must take 4 arguments, a pointer, 2 values, and a memory. +// They must also take an offset in Aux/AuxInt. +var pairableStores = map[Op]pairableStoreInfo{ + OpARM64MOVDstore: {8, OpARM64STP}, + OpARM64MOVWstore: {4, OpARM64STPW}, + OpARM64FMOVDstore: {8, OpARM64FSTPD}, + OpARM64FMOVSstore: {4, OpARM64FSTPS}, + // TODO: storezero variants. +} + +// offsetOk returns true if a pair instruction should be used +// for the offset Aux+off, when the data width (of the +// unpaired instructions) is width. +// This function is best-effort. The compiled function must +// still work if offsetOk always returns true. +// TODO: this is currently arm64-specific. +func offsetOk(aux Aux, off, width int64) bool { + if true { + // Seems to generate slightly smaller code if we just + // always allow this rewrite. + // + // Without pairing, we have 2 load instructions, like: + // LDR 88(R0), R1 + // LDR 96(R0), R2 + // with pairing we have, best case: + // LDP 88(R0), R1, R2 + // but maybe we need an adjuster if out of range or unaligned: + // ADD R0, $88, R27 + // LDP (R27), R1, R2 + // Even with the adjuster, it is at least no worse. + // + // A similar situation occurs when accessing globals. + // Two loads from globals requires 4 instructions, + // two ADRP and two LDR. With pairing, we need + // ADRP+ADD+LDP, three instructions. + // + // With pairing, it looks like the critical path might + // be a little bit longer. But it should never be more + // instructions. + // TODO: see if that longer critical path causes any + // regressions. + return true + } + if aux != nil { + if _, ok := aux.(*ir.Name); !ok { + // Offset is probably too big (globals). + return false + } + // We let *ir.Names pass here, as + // they are probably small offsets from SP. + // There's no guarantee that we're in range + // in that case though (we don't know the + // stack frame size yet), so the assembler + // might need to issue fixup instructions. + // Assume some small frame size. + if off >= 0 { + off += 120 + } + // TODO: figure out how often this helps vs. hurts. + } + switch width { + case 4: + if off >= -256 && off <= 252 && off%4 == 0 { + return true + } + case 8: + if off >= -512 && off <= 504 && off%8 == 0 { + return true + } + } + return false +} + +func pairLoads(f *Func) { + var loads []*Value + + // Registry of aux values for sorting. + auxIDs := map[Aux]int{} + auxID := func(aux Aux) int { + id, ok := auxIDs[aux] + if !ok { + id = len(auxIDs) + auxIDs[aux] = id + } + return id + } + + for _, b := range f.Blocks { + // Find loads. + loads = loads[:0] + clear(auxIDs) + for _, v := range b.Values { + info := pairableLoads[v.Op] + if info.width == 0 { + continue // not pairable + } + if !offsetOk(v.Aux, v.AuxInt, info.width) { + continue // not advisable + } + loads = append(loads, v) + } + if len(loads) < 2 { + continue + } + + // Sort to put pairable loads together. + slices.SortFunc(loads, func(x, y *Value) int { + // First sort by op, ptr, and memory arg. + if x.Op != y.Op { + return int(x.Op - y.Op) + } + if x.Args[0].ID != y.Args[0].ID { + return int(x.Args[0].ID - y.Args[0].ID) + } + if x.Args[1].ID != y.Args[1].ID { + return int(x.Args[1].ID - y.Args[1].ID) + } + // Then sort by aux. (nil first, then by aux ID) + if x.Aux != nil { + if y.Aux == nil { + return 1 + } + a, b := auxID(x.Aux), auxID(y.Aux) + if a != b { + return a - b + } + } else if y.Aux != nil { + return -1 + } + // Then sort by offset, low to high. + return int(x.AuxInt - y.AuxInt) + }) + + // Look for pairable loads. + for i := 0; i < len(loads)-1; i++ { + x := loads[i] + y := loads[i+1] + if x.Op != y.Op || x.Args[0] != y.Args[0] || x.Args[1] != y.Args[1] { + continue + } + if x.Aux != y.Aux { + continue + } + if x.AuxInt+pairableLoads[x.Op].width != y.AuxInt { + continue + } + + // Commit point. + + // Make the 2-register load. + load := b.NewValue2IA(x.Pos, pairableLoads[x.Op].pair, types.NewTuple(x.Type, y.Type), x.AuxInt, x.Aux, x.Args[0], x.Args[1]) + + // Modify x to be (Select0 load). Similar for y. + x.reset(OpSelect0) + x.SetArgs1(load) + y.reset(OpSelect1) + y.SetArgs1(load) + + i++ // Skip y next time around the loop. + } + } +} + +func pairStores(f *Func) { + last := f.Cache.allocBoolSlice(f.NumValues()) + defer f.Cache.freeBoolSlice(last) + + // prevStore returns the previous store in the + // same block, or nil if there are none. + prevStore := func(v *Value) *Value { + if v.Op == OpInitMem || v.Op == OpPhi { + return nil + } + m := v.MemoryArg() + if m.Block != v.Block { + return nil + } + return m + } + + for _, b := range f.Blocks { + // Find last store in block, so we can + // walk the stores last to first. + // Last to first helps ensure that the rewrites we + // perform do not get in the way of subsequent rewrites. + for _, v := range b.Values { + if v.Type.IsMemory() { + last[v.ID] = true + } + } + for _, v := range b.Values { + if v.Type.IsMemory() { + if m := prevStore(v); m != nil { + last[m.ID] = false + } + } + } + var lastMem *Value + for _, v := range b.Values { + if last[v.ID] { + lastMem = v + break + } + } + + // Check all stores, from last to first. + memCheck: + for v := lastMem; v != nil; v = prevStore(v) { + info := pairableStores[v.Op] + if info.width == 0 { + continue // Not pairable. + } + if !offsetOk(v.Aux, v.AuxInt, info.width) { + continue // Not advisable to pair. + } + ptr := v.Args[0] + val := v.Args[1] + mem := v.Args[2] + off := v.AuxInt + aux := v.Aux + + // Look for earlier store we can combine with. + lowerOk := true + higherOk := true + count := 10 // max lookback distance + for w := prevStore(v); w != nil; w = prevStore(w) { + if w.Uses != 1 { + // We can't combine stores if the earlier + // store has any use besides the next one + // in the store chain. + // (Unless we could check the aliasing of + // all those other uses.) + continue memCheck + } + if w.Op == v.Op && + w.Args[0] == ptr && + w.Aux == aux && + (lowerOk && w.AuxInt == off-info.width || higherOk && w.AuxInt == off+info.width) { + // This op is mergeable with v. + + // Commit point. + + // ptr val1 val2 mem + args := []*Value{ptr, val, w.Args[1], mem} + if w.AuxInt == off-info.width { + args[1], args[2] = args[2], args[1] + off -= info.width + } + v.reset(info.pair) + v.AddArgs(args...) + v.Aux = aux + v.AuxInt = off + v.Pos = w.Pos // take position of earlier of the two stores (TODO: not really working?) + + // Make w just a memory copy. + wmem := w.MemoryArg() + w.reset(OpCopy) + w.SetArgs1(wmem) + continue memCheck + } + if count--; count == 0 { + // Only look back so far. + // This keeps us in O(n) territory, and it + // also prevents us from keeping values + // in registers for too long (and thus + // needing to spill them). + continue memCheck + } + // We're now looking at a store w which is currently + // between the store v that we're intending to merge into, + // and the store we'll eventually find to merge with it. + // Make sure this store doesn't alias with the one + // we'll be moving. + var width int64 + switch w.Op { + case OpARM64MOVDstore, OpARM64MOVDstorezero, OpARM64FMOVDstore: + width = 8 + case OpARM64MOVWstore, OpARM64MOVWstorezero, OpARM64FMOVSstore: + width = 4 + case OpARM64MOVHstore, OpARM64MOVHstorezero: + width = 2 + case OpARM64MOVBstore, OpARM64MOVBstorezero: + width = 1 + case OpCopy: + continue // this was a store we merged earlier + default: + // Can't reorder with any other memory operations. + // (atomics, calls, ...) + continue memCheck + } + + // We only allow reordering with respect to other + // writes to the same pointer and aux, so we can + // compute the exact the aliasing relationship. + if w.Args[0] != ptr || w.Aux != aux { + continue memCheck + } + if overlap(w.AuxInt, width, off-info.width, info.width) { + // Aliases with slot before v's location. + lowerOk = false + } + if overlap(w.AuxInt, width, off+info.width, info.width) { + // Aliases with slot after v's location. + higherOk = false + } + if !higherOk && !lowerOk { + continue memCheck + } + } + } + } +} diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go index c3948dc9b1d370..9d2ee5ceedf9d7 100644 --- a/src/cmd/compile/internal/ssa/prove.go +++ b/src/cmd/compile/internal/ssa/prove.go @@ -1362,11 +1362,11 @@ func prove(f *Func) { start, end = end, start } - if !(start.Op == OpConst8 || start.Op == OpConst16 || start.Op == OpConst32 || start.Op == OpConst64) { + if !start.isGenericIntConst() { // if start is not a constant we would be winning nothing from inverting the loop continue } - if end.Op == OpConst8 || end.Op == OpConst16 || end.Op == OpConst32 || end.Op == OpConst64 { + if end.isGenericIntConst() { // TODO: if both start and end are constants we should rewrite such that the comparison // is against zero and nxt is ++ or -- operation // That means: @@ -1653,6 +1653,10 @@ func initLimit(v *Value) limit { case OpCtz8, OpBitLen8: lim = lim.unsignedMax(8) + // bool to uint8 conversion + case OpCvtBoolToUint8: + lim = lim.unsignedMax(1) + // length operations case OpStringLen, OpSliceLen, OpSliceCap: lim = lim.signedMin(0) diff --git a/src/cmd/compile/internal/ssa/regalloc.go b/src/cmd/compile/internal/ssa/regalloc.go index 08ce0d16a63bba..1b7bcb2b1d4c8f 100644 --- a/src/cmd/compile/internal/ssa/regalloc.go +++ b/src/cmd/compile/internal/ssa/regalloc.go @@ -930,6 +930,7 @@ func (s *regAllocState) regalloc(f *Func) { // Data structure used for computing desired registers. var desired desiredState + desiredSecondReg := map[ID][4]register{} // desired register allocation for 2nd part of a tuple // Desired registers for inputs & outputs for each instruction in the block. type dentry struct { @@ -949,6 +950,7 @@ func (s *regAllocState) regalloc(f *Func) { s.curBlock = b s.startRegsMask = 0 s.usedSinceBlockStart = 0 + clear(desiredSecondReg) // Initialize regValLiveSet and uses fields for this block. // Walk backwards through the block doing liveness analysis. @@ -1346,6 +1348,11 @@ func (s *regAllocState) regalloc(f *Func) { } dinfo[i].in[j] = desired.get(a.ID) } + if v.Op == OpSelect1 && prefs[0] != noRegister { + // Save desired registers of select1 for + // use by the tuple generating instruction. + desiredSecondReg[v.Args[0].ID] = prefs + } } // Process all the non-phi values. @@ -1748,6 +1755,17 @@ func (s *regAllocState) regalloc(f *Func) { } } } + if out.idx == 1 { + if prefs, ok := desiredSecondReg[v.ID]; ok { + for _, r := range prefs { + if r != noRegister && (mask&^s.used)>>r&1 != 0 { + // Desired register is allowed and unused. + mask = regMask(1) << r + break + } + } + } + } // Avoid registers we're saving for other values. if mask&^desired.avoid&^s.nospill&^s.used != 0 { mask &^= desired.avoid @@ -2874,7 +2892,8 @@ type desiredStateEntry struct { // Registers it would like to be in, in priority order. // Unused slots are filled with noRegister. // For opcodes that return tuples, we track desired registers only - // for the first element of the tuple. + // for the first element of the tuple (see desiredSecondReg for + // tracking the desired register for second part of a tuple). regs [4]register } diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 5630bfd72934d7..eb523675b15a6d 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -863,6 +863,12 @@ func disjoint(p1 *Value, n1 int64, p2 *Value, n2 int64) bool { } return base, offset } + + // Run types-based analysis + if disjointTypes(p1.Type, p2.Type) { + return true + } + p1, off1 := baseAndOffset(p1) p2, off2 := baseAndOffset(p2) if isSamePtr(p1, p2) { @@ -888,6 +894,39 @@ func disjoint(p1 *Value, n1 int64, p2 *Value, n2 int64) bool { return false } +// disjointTypes reports whether a memory region pointed to by a pointer of type +// t1 does not overlap with a memory region pointed to by a pointer of type t2 -- +// based on type aliasing rules. +func disjointTypes(t1 *types.Type, t2 *types.Type) bool { + // Unsafe pointer can alias with anything. + if t1.IsUnsafePtr() || t2.IsUnsafePtr() { + return false + } + + if !t1.IsPtr() || !t2.IsPtr() { + panic("disjointTypes: one of arguments is not a pointer") + } + + t1 = t1.Elem() + t2 = t2.Elem() + + // Not-in-heap types are not supported -- they are rare and non-important; also, + // type.HasPointers check doesn't work for them correctly. + if t1.NotInHeap() || t2.NotInHeap() { + return false + } + + isPtrShaped := func(t *types.Type) bool { return int(t.Size()) == types.PtrSize && t.HasPointers() } + + // Pointers and non-pointers are disjoint (https://pkg.go.dev/unsafe#Pointer). + if (isPtrShaped(t1) && !t2.HasPointers()) || + (isPtrShaped(t2) && !t1.HasPointers()) { + return true + } + + return false +} + // moveSize returns the number of bytes an aligned MOV instruction moves. func moveSize(align int64, c *Config) int64 { switch { @@ -964,6 +1003,14 @@ func clobber(vv ...*Value) bool { return true } +// resetCopy resets v to be a copy of arg. +// Always returns true. +func resetCopy(v *Value, arg *Value) bool { + v.reset(OpCopy) + v.AddArg(arg) + return true +} + // clobberIfDead resets v when use count is 1. Returns true. // clobberIfDead is used by rewrite rules to decrement // use counts of v's args when v is dead and never used. @@ -2424,3 +2471,86 @@ func rewriteStructStore(v *Value) *Value { return mem } + +// isDirectType reports whether v represents a type +// (a *runtime._type) whose value is stored directly in an +// interface (i.e., is pointer or pointer-like). +func isDirectType(v *Value) bool { + return isDirectType1(v) +} + +// v is a type +func isDirectType1(v *Value) bool { + switch v.Op { + case OpITab: + return isDirectType2(v.Args[0]) + case OpAddr: + lsym := v.Aux.(*obj.LSym) + if lsym.Extra == nil { + return false + } + if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok { + return types.IsDirectIface(ti.Type.(*types.Type)) + } + } + return false +} + +// v is an empty interface +func isDirectType2(v *Value) bool { + switch v.Op { + case OpIMake: + return isDirectType1(v.Args[0]) + } + return false +} + +// isDirectIface reports whether v represents an itab +// (a *runtime._itab) for a type whose value is stored directly +// in an interface (i.e., is pointer or pointer-like). +func isDirectIface(v *Value) bool { + return isDirectIface1(v, 9) +} + +// v is an itab +func isDirectIface1(v *Value, depth int) bool { + if depth == 0 { + return false + } + switch v.Op { + case OpITab: + return isDirectIface2(v.Args[0], depth-1) + case OpAddr: + lsym := v.Aux.(*obj.LSym) + if lsym.Extra == nil { + return false + } + if ii, ok := (*lsym.Extra).(*obj.ItabInfo); ok { + return types.IsDirectIface(ii.Type.(*types.Type)) + } + case OpConstNil: + // We can treat this as direct, because if the itab is + // nil, the data field must be nil also. + return true + } + return false +} + +// v is an interface +func isDirectIface2(v *Value, depth int) bool { + if depth == 0 { + return false + } + switch v.Op { + case OpIMake: + return isDirectIface1(v.Args[0], depth-1) + case OpPhi: + for _, a := range v.Args { + if !isDirectIface2(a, depth-1) { + return false + } + } + return true + } + return false +} diff --git a/src/cmd/compile/internal/ssa/rewrite386.go b/src/cmd/compile/internal/ssa/rewrite386.go index 9f1645f8c33dcc..dbc1335fcdc338 100644 --- a/src/cmd/compile/internal/ssa/rewrite386.go +++ b/src/cmd/compile/internal/ssa/rewrite386.go @@ -3491,6 +3491,19 @@ func rewriteValue386_Op386MOVBLSXload(v *Value) bool { v.AddArg2(base, mem) return true } + // match: (MOVBLSXload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVLconst [int32(int8(read8(sym, int64(off))))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(Op386MOVLconst) + v.AuxInt = int32ToAuxInt(int32(int8(read8(sym, int64(off))))) + return true + } return false } func rewriteValue386_Op386MOVBLZX(v *Value) bool { @@ -4672,6 +4685,19 @@ func rewriteValue386_Op386MOVWLSXload(v *Value) bool { v.AddArg2(base, mem) return true } + // match: (MOVWLSXload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVLconst [int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(Op386MOVLconst) + v.AuxInt = int32ToAuxInt(int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValue386_Op386MOVWLZX(v *Value) bool { diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index f17c4be5160775..be9b3d9ea98d38 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -1007,10 +1007,10 @@ func rewriteValueAMD64(v *Value) bool { v.Op = OpAMD64ROLB return true case OpRound32F: - v.Op = OpCopy + v.Op = OpAMD64LoweredRound32F return true case OpRound64F: - v.Op = OpCopy + v.Op = OpAMD64LoweredRound64F return true case OpRoundToEven: return rewriteValueAMD64_OpRoundToEven(v) @@ -1261,6 +1261,21 @@ func rewriteValueAMD64_OpAMD64ADCQconst(v *Value) bool { func rewriteValueAMD64_OpAMD64ADDL(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + // match: (ADDL (SHRLconst [1] x) (SHRLconst [1] x)) + // result: (ANDLconst [-2] x) + for { + if v_0.Op != OpAMD64SHRLconst || auxIntToInt8(v_0.AuxInt) != 1 { + break + } + x := v_0.Args[0] + if v_1.Op != OpAMD64SHRLconst || auxIntToInt8(v_1.AuxInt) != 1 || x != v_1.Args[0] { + break + } + v.reset(OpAMD64ANDLconst) + v.AuxInt = int32ToAuxInt(-2) + v.AddArg(x) + return true + } // match: (ADDL x (MOVLconst [c])) // result: (ADDLconst [c] x) for { @@ -1307,21 +1322,6 @@ func rewriteValueAMD64_OpAMD64ADDL(v *Value) bool { } break } - // match: (ADDL x (SHLLconst [1] y)) - // result: (LEAL2 x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - if v_1.Op != OpAMD64SHLLconst || auxIntToInt8(v_1.AuxInt) != 1 { - continue - } - y := v_1.Args[0] - v.reset(OpAMD64LEAL2) - v.AddArg2(x, y) - return true - } - break - } // match: (ADDL x (ADDL y y)) // result: (LEAL2 x y) for { @@ -1461,14 +1461,17 @@ func rewriteValueAMD64_OpAMD64ADDLconst(v *Value) bool { v.AddArg2(x, y) return true } - // match: (ADDLconst [c] (SHLLconst [1] x)) + // match: (ADDLconst [c] (ADDL x x)) // result: (LEAL1 [c] x x) for { c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpAMD64SHLLconst || auxIntToInt8(v_0.AuxInt) != 1 { + if v_0.Op != OpAMD64ADDL { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] { break } - x := v_0.Args[0] v.reset(OpAMD64LEAL1) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, x) @@ -1806,6 +1809,21 @@ func rewriteValueAMD64_OpAMD64ADDLmodify(v *Value) bool { func rewriteValueAMD64_OpAMD64ADDQ(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + // match: (ADDQ (SHRQconst [1] x) (SHRQconst [1] x)) + // result: (ANDQconst [-2] x) + for { + if v_0.Op != OpAMD64SHRQconst || auxIntToInt8(v_0.AuxInt) != 1 { + break + } + x := v_0.Args[0] + if v_1.Op != OpAMD64SHRQconst || auxIntToInt8(v_1.AuxInt) != 1 || x != v_1.Args[0] { + break + } + v.reset(OpAMD64ANDQconst) + v.AuxInt = int32ToAuxInt(-2) + v.AddArg(x) + return true + } // match: (ADDQ x (MOVQconst [c])) // cond: is32Bit(c) && !t.IsPtr() // result: (ADDQconst [int32(c)] x) @@ -1873,21 +1891,6 @@ func rewriteValueAMD64_OpAMD64ADDQ(v *Value) bool { } break } - // match: (ADDQ x (SHLQconst [1] y)) - // result: (LEAQ2 x y) - for { - for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { - x := v_0 - if v_1.Op != OpAMD64SHLQconst || auxIntToInt8(v_1.AuxInt) != 1 { - continue - } - y := v_1.Args[0] - v.reset(OpAMD64LEAQ2) - v.AddArg2(x, y) - return true - } - break - } // match: (ADDQ x (ADDQ y y)) // result: (LEAQ2 x y) for { @@ -2052,14 +2055,17 @@ func rewriteValueAMD64_OpAMD64ADDQconst(v *Value) bool { v.AddArg2(x, y) return true } - // match: (ADDQconst [c] (SHLQconst [1] x)) + // match: (ADDQconst [c] (ADDQ x x)) // result: (LEAQ1 [c] x x) for { c := auxIntToInt32(v.AuxInt) - if v_0.Op != OpAMD64SHLQconst || auxIntToInt8(v_0.AuxInt) != 1 { + if v_0.Op != OpAMD64ADDQ { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] { break } - x := v_0.Args[0] v.reset(OpAMD64LEAQ1) v.AuxInt = int32ToAuxInt(c) v.AddArg2(x, x) @@ -2424,6 +2430,26 @@ func rewriteValueAMD64_OpAMD64ADDSD(v *Value) bool { } break } + // match: (ADDSD (MULSD x y) z) + // cond: buildcfg.GOAMD64 >= 3 && z.Block.Func.useFMA(v) + // result: (VFMADD231SD z x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64MULSD { + continue + } + y := v_0.Args[1] + x := v_0.Args[0] + z := v_1 + if !(buildcfg.GOAMD64 >= 3 && z.Block.Func.useFMA(v)) { + continue + } + v.reset(OpAMD64VFMADD231SD) + v.AddArg3(z, x, y) + return true + } + break + } return false } func rewriteValueAMD64_OpAMD64ADDSDload(v *Value) bool { @@ -2527,6 +2553,26 @@ func rewriteValueAMD64_OpAMD64ADDSS(v *Value) bool { } break } + // match: (ADDSS (MULSS x y) z) + // cond: buildcfg.GOAMD64 >= 3 && z.Block.Func.useFMA(v) + // result: (VFMADD231SS z x y) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpAMD64MULSS { + continue + } + y := v_0.Args[1] + x := v_0.Args[0] + z := v_1 + if !(buildcfg.GOAMD64 >= 3 && z.Block.Func.useFMA(v)) { + continue + } + v.reset(OpAMD64VFMADD231SS) + v.AddArg3(z, x, y) + return true + } + break + } return false } func rewriteValueAMD64_OpAMD64ADDSSload(v *Value) bool { @@ -3637,6 +3683,23 @@ func rewriteValueAMD64_OpAMD64BTLconst(v *Value) bool { v.AddArg(x) return true } + // match: (BTLconst [c] (ADDQ x x)) + // cond: c>1 + // result: (BTLconst [c-1] x) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64ADDQ { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] || !(c > 1) { + break + } + v.reset(OpAMD64BTLconst) + v.AuxInt = int8ToAuxInt(c - 1) + v.AddArg(x) + return true + } // match: (BTLconst [c] (SHLQconst [d] x)) // cond: c>d // result: (BTLconst [c-d] x) @@ -3689,6 +3752,23 @@ func rewriteValueAMD64_OpAMD64BTLconst(v *Value) bool { v.AddArg(x) return true } + // match: (BTLconst [c] (ADDL x x)) + // cond: c>1 + // result: (BTLconst [c-1] x) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64ADDL { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] || !(c > 1) { + break + } + v.reset(OpAMD64BTLconst) + v.AuxInt = int8ToAuxInt(c - 1) + v.AddArg(x) + return true + } // match: (BTLconst [c] (SHLLconst [d] x)) // cond: c>d // result: (BTLconst [c-d] x) @@ -3761,6 +3841,23 @@ func rewriteValueAMD64_OpAMD64BTQconst(v *Value) bool { v.AddArg(x) return true } + // match: (BTQconst [c] (ADDQ x x)) + // cond: c>1 + // result: (BTQconst [c-1] x) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64ADDQ { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] || !(c > 1) { + break + } + v.reset(OpAMD64BTQconst) + v.AuxInt = int8ToAuxInt(c - 1) + v.AddArg(x) + return true + } // match: (BTQconst [c] (SHLQconst [d] x)) // cond: c>d // result: (BTQconst [c-d] x) @@ -4149,6 +4246,7 @@ func rewriteValueAMD64_OpAMD64CMOVLGE(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (CMOVLGE x y (InvertFlags cond)) // result: (CMOVLLE x y cond) for { @@ -4212,6 +4310,48 @@ func rewriteValueAMD64_OpAMD64CMOVLGE(v *Value) bool { v.copyOf(y) return true } + // match: (CMOVLGE x y c:(CMPQconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVLGT x y (CMPQconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVLGT) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } + // match: (CMOVLGE x y c:(CMPLconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVLGT x y (CMPLconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVLGT) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } return false } func rewriteValueAMD64_OpAMD64CMOVLGT(v *Value) bool { @@ -4494,6 +4634,7 @@ func rewriteValueAMD64_OpAMD64CMOVLLT(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (CMOVLLT x y (InvertFlags cond)) // result: (CMOVLGT x y cond) for { @@ -4557,6 +4698,48 @@ func rewriteValueAMD64_OpAMD64CMOVLLT(v *Value) bool { v.copyOf(x) return true } + // match: (CMOVLLT x y c:(CMPQconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVLLE x y (CMPQconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVLLE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } + // match: (CMOVLLT x y c:(CMPLconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVLLE x y (CMPLconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVLLE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } return false } func rewriteValueAMD64_OpAMD64CMOVLNE(v *Value) bool { @@ -4999,6 +5182,7 @@ func rewriteValueAMD64_OpAMD64CMOVQGE(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (CMOVQGE x y (InvertFlags cond)) // result: (CMOVQLE x y cond) for { @@ -5062,6 +5246,48 @@ func rewriteValueAMD64_OpAMD64CMOVQGE(v *Value) bool { v.copyOf(y) return true } + // match: (CMOVQGE x y c:(CMPQconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVQGT x y (CMPQconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVQGT) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } + // match: (CMOVQGE x y c:(CMPLconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVQGT x y (CMPLconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVQGT) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } return false } func rewriteValueAMD64_OpAMD64CMOVQGT(v *Value) bool { @@ -5344,6 +5570,7 @@ func rewriteValueAMD64_OpAMD64CMOVQLT(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block // match: (CMOVQLT x y (InvertFlags cond)) // result: (CMOVQGT x y cond) for { @@ -5407,6 +5634,48 @@ func rewriteValueAMD64_OpAMD64CMOVQLT(v *Value) bool { v.copyOf(x) return true } + // match: (CMOVQLT x y c:(CMPQconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVQLE x y (CMPQconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVQLE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } + // match: (CMOVQLT x y c:(CMPLconst [128] z)) + // cond: c.Uses == 1 + // result: (CMOVQLE x y (CMPLconst [127] z)) + for { + x := v_0 + y := v_1 + c := v_2 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64CMOVQLE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + v.AddArg3(x, y, v0) + return true + } return false } func rewriteValueAMD64_OpAMD64CMOVQNE(v *Value) bool { @@ -8287,17 +8556,20 @@ func rewriteValueAMD64_OpAMD64LEAL1(v *Value) bool { } break } - // match: (LEAL1 [c] {s} x (SHLLconst [1] y)) + // match: (LEAL1 [c] {s} x (ADDL y y)) // result: (LEAL2 [c] {s} x y) for { c := auxIntToInt32(v.AuxInt) s := auxToSym(v.Aux) for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 - if v_1.Op != OpAMD64SHLLconst || auxIntToInt8(v_1.AuxInt) != 1 { + if v_1.Op != OpAMD64ADDL { + continue + } + y := v_1.Args[1] + if y != v_1.Args[0] { continue } - y := v_1.Args[0] v.reset(OpAMD64LEAL2) v.AuxInt = int32ToAuxInt(c) v.Aux = symToAux(s) @@ -8391,16 +8663,19 @@ func rewriteValueAMD64_OpAMD64LEAL2(v *Value) bool { v.AddArg2(x, y) return true } - // match: (LEAL2 [c] {s} x (SHLLconst [1] y)) + // match: (LEAL2 [c] {s} x (ADDL y y)) // result: (LEAL4 [c] {s} x y) for { c := auxIntToInt32(v.AuxInt) s := auxToSym(v.Aux) x := v_0 - if v_1.Op != OpAMD64SHLLconst || auxIntToInt8(v_1.AuxInt) != 1 { + if v_1.Op != OpAMD64ADDL { + break + } + y := v_1.Args[1] + if y != v_1.Args[0] { break } - y := v_1.Args[0] v.reset(OpAMD64LEAL4) v.AuxInt = int32ToAuxInt(c) v.Aux = symToAux(s) @@ -8423,6 +8698,26 @@ func rewriteValueAMD64_OpAMD64LEAL2(v *Value) bool { v.AddArg2(x, y) return true } + // match: (LEAL2 [0] {s} (ADDL x x) x) + // cond: s == nil + // result: (SHLLconst [2] x) + for { + if auxIntToInt32(v.AuxInt) != 0 { + break + } + s := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDL { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] || x != v_1 || !(s == nil) { + break + } + v.reset(OpAMD64SHLLconst) + v.AuxInt = int8ToAuxInt(2) + v.AddArg(x) + return true + } return false } func rewriteValueAMD64_OpAMD64LEAL4(v *Value) bool { @@ -8470,16 +8765,19 @@ func rewriteValueAMD64_OpAMD64LEAL4(v *Value) bool { v.AddArg2(x, y) return true } - // match: (LEAL4 [c] {s} x (SHLLconst [1] y)) + // match: (LEAL4 [c] {s} x (ADDL y y)) // result: (LEAL8 [c] {s} x y) for { c := auxIntToInt32(v.AuxInt) s := auxToSym(v.Aux) x := v_0 - if v_1.Op != OpAMD64SHLLconst || auxIntToInt8(v_1.AuxInt) != 1 { + if v_1.Op != OpAMD64ADDL { + break + } + y := v_1.Args[1] + if y != v_1.Args[0] { break } - y := v_1.Args[0] v.reset(OpAMD64LEAL8) v.AuxInt = int32ToAuxInt(c) v.Aux = symToAux(s) @@ -8721,17 +9019,20 @@ func rewriteValueAMD64_OpAMD64LEAQ1(v *Value) bool { } break } - // match: (LEAQ1 [c] {s} x (SHLQconst [1] y)) + // match: (LEAQ1 [c] {s} x (ADDQ y y)) // result: (LEAQ2 [c] {s} x y) for { c := auxIntToInt32(v.AuxInt) s := auxToSym(v.Aux) for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { x := v_0 - if v_1.Op != OpAMD64SHLQconst || auxIntToInt8(v_1.AuxInt) != 1 { + if v_1.Op != OpAMD64ADDQ { + continue + } + y := v_1.Args[1] + if y != v_1.Args[0] { continue } - y := v_1.Args[0] v.reset(OpAMD64LEAQ2) v.AuxInt = int32ToAuxInt(c) v.Aux = symToAux(s) @@ -8924,16 +9225,19 @@ func rewriteValueAMD64_OpAMD64LEAQ2(v *Value) bool { v.AddArg2(x, y) return true } - // match: (LEAQ2 [c] {s} x (SHLQconst [1] y)) + // match: (LEAQ2 [c] {s} x (ADDQ y y)) // result: (LEAQ4 [c] {s} x y) for { c := auxIntToInt32(v.AuxInt) s := auxToSym(v.Aux) x := v_0 - if v_1.Op != OpAMD64SHLQconst || auxIntToInt8(v_1.AuxInt) != 1 { + if v_1.Op != OpAMD64ADDQ { + break + } + y := v_1.Args[1] + if y != v_1.Args[0] { break } - y := v_1.Args[0] v.reset(OpAMD64LEAQ4) v.AuxInt = int32ToAuxInt(c) v.Aux = symToAux(s) @@ -8956,6 +9260,26 @@ func rewriteValueAMD64_OpAMD64LEAQ2(v *Value) bool { v.AddArg2(x, y) return true } + // match: (LEAQ2 [0] {s} (ADDQ x x) x) + // cond: s == nil + // result: (SHLQconst [2] x) + for { + if auxIntToInt32(v.AuxInt) != 0 { + break + } + s := auxToSym(v.Aux) + if v_0.Op != OpAMD64ADDQ { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] || x != v_1 || !(s == nil) { + break + } + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(2) + v.AddArg(x) + return true + } // match: (LEAQ2 [off1] {sym1} (LEAQ [off2] {sym2} x) y) // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && x.Op != OpSB // result: (LEAQ2 [off1+off2] {mergeSym(sym1,sym2)} x y) @@ -9087,16 +9411,19 @@ func rewriteValueAMD64_OpAMD64LEAQ4(v *Value) bool { v.AddArg2(x, y) return true } - // match: (LEAQ4 [c] {s} x (SHLQconst [1] y)) + // match: (LEAQ4 [c] {s} x (ADDQ y y)) // result: (LEAQ8 [c] {s} x y) for { c := auxIntToInt32(v.AuxInt) s := auxToSym(v.Aux) x := v_0 - if v_1.Op != OpAMD64SHLQconst || auxIntToInt8(v_1.AuxInt) != 1 { + if v_1.Op != OpAMD64ADDQ { + break + } + y := v_1.Args[1] + if y != v_1.Args[0] { break } - y := v_1.Args[0] v.reset(OpAMD64LEAQ8) v.AuxInt = int32ToAuxInt(c) v.Aux = symToAux(s) @@ -9553,6 +9880,19 @@ func rewriteValueAMD64_OpAMD64MOVBQSXload(v *Value) bool { v.AddArg2(base, mem) return true } + // match: (MOVBQSXload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVQconst [int64(int8(read8(sym, int64(off))))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(int8(read8(sym, int64(off))))) + return true + } return false } func rewriteValueAMD64_OpAMD64MOVBQZX(v *Value) bool { @@ -10297,6 +10637,8 @@ func rewriteValueAMD64_OpAMD64MOVLQSX(v *Value) bool { func rewriteValueAMD64_OpAMD64MOVLQSXload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVLQSXload [off] {sym} ptr (MOVLstore [off2] {sym2} ptr2 x _)) // cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) // result: (MOVLQSX x) @@ -10340,6 +10682,19 @@ func rewriteValueAMD64_OpAMD64MOVLQSXload(v *Value) bool { v.AddArg2(base, mem) return true } + // match: (MOVLQSXload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVQconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValueAMD64_OpAMD64MOVLQZX(v *Value) bool { @@ -10627,15 +10982,15 @@ func rewriteValueAMD64_OpAMD64MOVLload(v *Value) bool { } // match: (MOVLload [off] {sym} (SB) _) // cond: symIsRO(sym) - // result: (MOVQconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) + // result: (MOVLconst [int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) if v_0.Op != OpSB || !(symIsRO(sym)) { break } - v.reset(OpAMD64MOVQconst) - v.AuxInt = int64ToAuxInt(int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))) + v.reset(OpAMD64MOVLconst) + v.AuxInt = int32ToAuxInt(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))) return true } return false @@ -12677,6 +13032,8 @@ func rewriteValueAMD64_OpAMD64MOVWQSX(v *Value) bool { func rewriteValueAMD64_OpAMD64MOVWQSXload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWQSXload [off] {sym} ptr (MOVWstore [off2] {sym2} ptr2 x _)) // cond: sym == sym2 && off == off2 && isSamePtr(ptr, ptr2) // result: (MOVWQSX x) @@ -12720,6 +13077,19 @@ func rewriteValueAMD64_OpAMD64MOVWQSXload(v *Value) bool { v.AddArg2(base, mem) return true } + // match: (MOVWQSXload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVQconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpAMD64MOVQconst) + v.AuxInt = int64ToAuxInt(int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValueAMD64_OpAMD64MOVWQZX(v *Value) bool { @@ -16503,6 +16873,8 @@ func rewriteValueAMD64_OpAMD64SETA(v *Value) bool { } func rewriteValueAMD64_OpAMD64SETAE(v *Value) bool { v_0 := v.Args[0] + b := v.Block + typ := &b.Func.Config.Types // match: (SETAE (TESTQ x x)) // result: (ConstBool [true]) for { @@ -16559,6 +16931,74 @@ func rewriteValueAMD64_OpAMD64SETAE(v *Value) bool { v.AuxInt = boolToAuxInt(true) return true } + // match: (SETAE (BTLconst [0] x)) + // result: (XORLconst [1] (ANDLconst [1] x)) + for { + if v_0.Op != OpAMD64BTLconst || auxIntToInt8(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + v.reset(OpAMD64XORLconst) + v.AuxInt = int32ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpAMD64ANDLconst, typ.Bool) + v0.AuxInt = int32ToAuxInt(1) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (SETAE (BTQconst [0] x)) + // result: (XORLconst [1] (ANDLconst [1] x)) + for { + if v_0.Op != OpAMD64BTQconst || auxIntToInt8(v_0.AuxInt) != 0 { + break + } + x := v_0.Args[0] + v.reset(OpAMD64XORLconst) + v.AuxInt = int32ToAuxInt(1) + v0 := b.NewValue0(v.Pos, OpAMD64ANDLconst, typ.Bool) + v0.AuxInt = int32ToAuxInt(1) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (SETAE c:(CMPQconst [128] x)) + // cond: c.Uses == 1 + // result: (SETA (CMPQconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETA) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (SETAE c:(CMPLconst [128] x)) + // cond: c.Uses == 1 + // result: (SETA (CMPLconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETA) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } // match: (SETAE (InvertFlags x)) // result: (SETBE x) for { @@ -16944,6 +17384,7 @@ func rewriteValueAMD64_OpAMD64SETAstore(v *Value) bool { } func rewriteValueAMD64_OpAMD64SETB(v *Value) bool { v_0 := v.Args[0] + b := v.Block // match: (SETB (TESTQ x x)) // result: (ConstBool [false]) for { @@ -17024,6 +17465,44 @@ func rewriteValueAMD64_OpAMD64SETB(v *Value) bool { v.AddArg(x) return true } + // match: (SETB c:(CMPQconst [128] x)) + // cond: c.Uses == 1 + // result: (SETBE (CMPQconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETBE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (SETB c:(CMPLconst [128] x)) + // cond: c.Uses == 1 + // result: (SETBE (CMPLconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETBE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } // match: (SETB (InvertFlags x)) // result: (SETA x) for { @@ -18583,6 +19062,45 @@ func rewriteValueAMD64_OpAMD64SETG(v *Value) bool { } func rewriteValueAMD64_OpAMD64SETGE(v *Value) bool { v_0 := v.Args[0] + b := v.Block + // match: (SETGE c:(CMPQconst [128] x)) + // cond: c.Uses == 1 + // result: (SETG (CMPQconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETG) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (SETGE c:(CMPLconst [128] x)) + // cond: c.Uses == 1 + // result: (SETG (CMPLconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETG) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } // match: (SETGE (InvertFlags x)) // result: (SETLE x) for { @@ -18968,6 +19486,45 @@ func rewriteValueAMD64_OpAMD64SETGstore(v *Value) bool { } func rewriteValueAMD64_OpAMD64SETL(v *Value) bool { v_0 := v.Args[0] + b := v.Block + // match: (SETL c:(CMPQconst [128] x)) + // cond: c.Uses == 1 + // result: (SETLE (CMPQconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPQconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETLE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } + // match: (SETL c:(CMPLconst [128] x)) + // cond: c.Uses == 1 + // result: (SETLE (CMPLconst [127] x)) + for { + c := v_0 + if c.Op != OpAMD64CMPLconst || auxIntToInt32(c.AuxInt) != 128 { + break + } + x := c.Args[0] + if !(c.Uses == 1) { + break + } + v.reset(OpAMD64SETLE) + v0 := b.NewValue0(v.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(x) + v.AddArg(v0) + return true + } // match: (SETL (InvertFlags x)) // result: (SETG x) for { @@ -20704,26 +21261,41 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool { } func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool { v_0 := v.Args[0] - // match: (SHLLconst [1] (SHRLconst [1] x)) - // result: (ANDLconst [-2] x) + // match: (SHLLconst x [0]) + // result: x for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHRLconst || auxIntToInt8(v_0.AuxInt) != 1 { + if auxIntToInt8(v.AuxInt) != 0 { break } - x := v_0.Args[0] - v.reset(OpAMD64ANDLconst) - v.AuxInt = int32ToAuxInt(-2) - v.AddArg(x) + x := v_0 + v.copyOf(x) return true } - // match: (SHLLconst x [0]) - // result: x + // match: (SHLLconst [1] x) + // result: (ADDL x x) for { - if auxIntToInt8(v.AuxInt) != 0 { + if auxIntToInt8(v.AuxInt) != 1 { break } x := v_0 - v.copyOf(x) + v.reset(OpAMD64ADDL) + v.AddArg2(x, x) + return true + } + // match: (SHLLconst [c] (ADDL x x)) + // result: (SHLLconst [c+1] x) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64ADDL { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] { + break + } + v.reset(OpAMD64SHLLconst) + v.AuxInt = int8ToAuxInt(c + 1) + v.AddArg(x) return true } // match: (SHLLconst [d] (MOVLconst [c])) @@ -20960,26 +21532,41 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool { } func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool { v_0 := v.Args[0] - // match: (SHLQconst [1] (SHRQconst [1] x)) - // result: (ANDQconst [-2] x) + // match: (SHLQconst x [0]) + // result: x for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHRQconst || auxIntToInt8(v_0.AuxInt) != 1 { + if auxIntToInt8(v.AuxInt) != 0 { break } - x := v_0.Args[0] - v.reset(OpAMD64ANDQconst) - v.AuxInt = int32ToAuxInt(-2) - v.AddArg(x) + x := v_0 + v.copyOf(x) return true } - // match: (SHLQconst x [0]) - // result: x + // match: (SHLQconst [1] x) + // result: (ADDQ x x) for { - if auxIntToInt8(v.AuxInt) != 0 { + if auxIntToInt8(v.AuxInt) != 1 { break } x := v_0 - v.copyOf(x) + v.reset(OpAMD64ADDQ) + v.AddArg2(x, x) + return true + } + // match: (SHLQconst [c] (ADDQ x x)) + // result: (SHLQconst [c+1] x) + for { + c := auxIntToInt8(v.AuxInt) + if v_0.Op != OpAMD64ADDQ { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] { + break + } + v.reset(OpAMD64SHLQconst) + v.AuxInt = int8ToAuxInt(c + 1) + v.AddArg(x) return true } // match: (SHLQconst [d] (MOVQconst [c])) @@ -21387,13 +21974,16 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool { } func rewriteValueAMD64_OpAMD64SHRLconst(v *Value) bool { v_0 := v.Args[0] - // match: (SHRLconst [1] (SHLLconst [1] x)) + // match: (SHRLconst [1] (ADDL x x)) // result: (ANDLconst [0x7fffffff] x) for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHLLconst || auxIntToInt8(v_0.AuxInt) != 1 { + if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64ADDL { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] { break } - x := v_0.Args[0] v.reset(OpAMD64ANDLconst) v.AuxInt = int32ToAuxInt(0x7fffffff) v.AddArg(x) @@ -21631,13 +22221,16 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool { } func rewriteValueAMD64_OpAMD64SHRQconst(v *Value) bool { v_0 := v.Args[0] - // match: (SHRQconst [1] (SHLQconst [1] x)) + // match: (SHRQconst [1] (ADDQ x x)) // result: (BTRQconst [63] x) for { - if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64SHLQconst || auxIntToInt8(v_0.AuxInt) != 1 { + if auxIntToInt8(v.AuxInt) != 1 || v_0.Op != OpAMD64ADDQ { + break + } + x := v_0.Args[1] + if x != v_0.Args[0] { break } - x := v_0.Args[0] v.reset(OpAMD64BTRQconst) v.AuxInt = int8ToAuxInt(63) v.AddArg(x) @@ -30238,34 +30831,13 @@ func rewriteValueAMD64_OpZero(v *Value) bool { return true } // match: (Zero [s] destptr mem) - // cond: s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE - // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) - for { - s := auxIntToInt64(v.AuxInt) - destptr := v_0 - mem := v_1 - if !(s%16 != 0 && s > 16 && s%16 > 8 && config.useSSE) { - break - } - v.reset(OpZero) - v.AuxInt = int64ToAuxInt(s - s%16) - v0 := b.NewValue0(v.Pos, OpOffPtr, destptr.Type) - v0.AuxInt = int64ToAuxInt(s % 16) - v0.AddArg(destptr) - v1 := b.NewValue0(v.Pos, OpAMD64MOVOstoreconst, types.TypeMem) - v1.AuxInt = valAndOffToAuxInt(makeValAndOff(0, 0)) - v1.AddArg2(destptr, mem) - v.AddArg2(v0, v1) - return true - } - // match: (Zero [s] destptr mem) - // cond: s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE + // cond: s%16 != 0 && s > 16 && config.useSSE // result: (Zero [s-s%16] (OffPtr destptr [s%16]) (MOVOstoreconst [makeValAndOff(0,0)] destptr mem)) for { s := auxIntToInt64(v.AuxInt) destptr := v_0 mem := v_1 - if !(s%16 != 0 && s > 16 && s%16 <= 8 && config.useSSE) { + if !(s%16 != 0 && s > 16 && config.useSSE) { break } v.reset(OpZero) @@ -30770,6 +31342,42 @@ func rewriteBlockAMD64(b *Block) bool { break } case BlockAMD64GE: + // match: (GE c:(CMPQconst [128] z) yes no) + // cond: c.Uses == 1 + // result: (GT (CMPQconst [127] z) yes no) + for b.Controls[0].Op == OpAMD64CMPQconst { + c := b.Controls[0] + if auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v0 := b.NewValue0(c.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + b.resetWithControl(BlockAMD64GT, v0) + return true + } + // match: (GE c:(CMPLconst [128] z) yes no) + // cond: c.Uses == 1 + // result: (GT (CMPLconst [127] z) yes no) + for b.Controls[0].Op == OpAMD64CMPLconst { + c := b.Controls[0] + if auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v0 := b.NewValue0(c.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + b.resetWithControl(BlockAMD64GT, v0) + return true + } // match: (GE (InvertFlags cmp) yes no) // result: (LE cmp yes no) for b.Controls[0].Op == OpAMD64InvertFlags { @@ -31037,6 +31645,42 @@ func rewriteBlockAMD64(b *Block) bool { return true } case BlockAMD64LT: + // match: (LT c:(CMPQconst [128] z) yes no) + // cond: c.Uses == 1 + // result: (LE (CMPQconst [127] z) yes no) + for b.Controls[0].Op == OpAMD64CMPQconst { + c := b.Controls[0] + if auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v0 := b.NewValue0(c.Pos, OpAMD64CMPQconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + b.resetWithControl(BlockAMD64LE, v0) + return true + } + // match: (LT c:(CMPLconst [128] z) yes no) + // cond: c.Uses == 1 + // result: (LE (CMPLconst [127] z) yes no) + for b.Controls[0].Op == OpAMD64CMPLconst { + c := b.Controls[0] + if auxIntToInt32(c.AuxInt) != 128 { + break + } + z := c.Args[0] + if !(c.Uses == 1) { + break + } + v0 := b.NewValue0(c.Pos, OpAMD64CMPLconst, types.TypeFlags) + v0.AuxInt = int32ToAuxInt(127) + v0.AddArg(z) + b.resetWithControl(BlockAMD64LE, v0) + return true + } // match: (LT (InvertFlags cmp) yes no) // result: (GT cmp yes no) for b.Controls[0].Op == OpAMD64InvertFlags { diff --git a/src/cmd/compile/internal/ssa/rewriteARM.go b/src/cmd/compile/internal/ssa/rewriteARM.go index 09be5ccf685b61..8dfa9ab6d6f20a 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM.go +++ b/src/cmd/compile/internal/ssa/rewriteARM.go @@ -4863,6 +4863,19 @@ func rewriteValueARM_OpARMMOVBload(v *Value) bool { v.AddArg3(ptr, idx, mem) return true } + // match: (MOVBload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVWconst [int32(int8(read8(sym, int64(off))))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpARMMOVWconst) + v.AuxInt = int32ToAuxInt(int32(int8(read8(sym, int64(off))))) + return true + } return false } func rewriteValueARM_OpARMMOVBloadidx(v *Value) bool { @@ -5700,6 +5713,8 @@ func rewriteValueARM_OpARMMOVHUreg(v *Value) bool { func rewriteValueARM_OpARMMOVHload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHload [off1] {sym} (ADDconst [off2] ptr) mem) // result: (MOVHload [off1+off2] {sym} ptr mem) for { @@ -5798,6 +5813,19 @@ func rewriteValueARM_OpARMMOVHload(v *Value) bool { v.AddArg3(ptr, idx, mem) return true } + // match: (MOVHload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVWconst [int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpARMMOVWconst) + v.AuxInt = int32ToAuxInt(int32(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValueARM_OpARMMOVHloadidx(v *Value) bool { diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index 6fabb77c0d804a..def0003764d3c5 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -8718,6 +8718,19 @@ func rewriteValueARM64_OpARM64MOVBload(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } + // match: (MOVBload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVDconst [int64(int8(read8(sym, int64(off))))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(int64(int8(read8(sym, int64(off))))) + return true + } return false } func rewriteValueARM64_OpARM64MOVBloadidx(v *Value) bool { @@ -10563,6 +10576,19 @@ func rewriteValueARM64_OpARM64MOVHload(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } + // match: (MOVHload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVDconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValueARM64_OpARM64MOVHloadidx(v *Value) bool { @@ -11978,6 +12004,19 @@ func rewriteValueARM64_OpARM64MOVWload(v *Value) bool { v.AuxInt = int64ToAuxInt(0) return true } + // match: (MOVWload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVDconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpARM64MOVDconst) + v.AuxInt = int64ToAuxInt(int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValueARM64_OpARM64MOVWloadidx(v *Value) bool { diff --git a/src/cmd/compile/internal/ssa/rewriteMIPS64.go b/src/cmd/compile/internal/ssa/rewriteMIPS64.go index bad8016cb4b04a..b82f027a5a801a 100644 --- a/src/cmd/compile/internal/ssa/rewriteMIPS64.go +++ b/src/cmd/compile/internal/ssa/rewriteMIPS64.go @@ -2802,6 +2802,19 @@ func rewriteValueMIPS64_OpMIPS64MOVBUload(v *Value) bool { v.AddArg2(ptr, mem) return true } + // match: (MOVBUload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVVconst [int64(read8(sym, int64(off)))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpMIPS64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(read8(sym, int64(off)))) + return true + } return false } func rewriteValueMIPS64_OpMIPS64MOVBUreg(v *Value) bool { @@ -2891,7 +2904,7 @@ func rewriteValueMIPS64_OpMIPS64MOVBload(v *Value) bool { } // match: (MOVBload [off] {sym} (SB) _) // cond: symIsRO(sym) - // result: (MOVVconst [int64(read8(sym, int64(off)))]) + // result: (MOVVconst [int64(int8(read8(sym, int64(off))))]) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -2899,7 +2912,7 @@ func rewriteValueMIPS64_OpMIPS64MOVBload(v *Value) bool { break } v.reset(OpMIPS64MOVVconst) - v.AuxInt = int64ToAuxInt(int64(read8(sym, int64(off)))) + v.AuxInt = int64ToAuxInt(int64(int8(read8(sym, int64(off))))) return true } return false @@ -3484,6 +3497,19 @@ func rewriteValueMIPS64_OpMIPS64MOVHUload(v *Value) bool { v.AddArg2(ptr, mem) return true } + // match: (MOVHUload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVVconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpMIPS64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))) + return true + } return false } func rewriteValueMIPS64_OpMIPS64MOVHUreg(v *Value) bool { @@ -3595,7 +3621,7 @@ func rewriteValueMIPS64_OpMIPS64MOVHload(v *Value) bool { } // match: (MOVHload [off] {sym} (SB) _) // cond: symIsRO(sym) - // result: (MOVVconst [int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))]) + // result: (MOVVconst [int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -3603,7 +3629,7 @@ func rewriteValueMIPS64_OpMIPS64MOVHload(v *Value) bool { break } v.reset(OpMIPS64MOVVconst) - v.AuxInt = int64ToAuxInt(int64(read16(sym, int64(off), config.ctxt.Arch.ByteOrder))) + v.AuxInt = int64ToAuxInt(int64(int16(read16(sym, int64(off), config.ctxt.Arch.ByteOrder)))) return true } return false @@ -4202,6 +4228,19 @@ func rewriteValueMIPS64_OpMIPS64MOVWUload(v *Value) bool { v.AddArg2(ptr, mem) return true } + // match: (MOVWUload [off] {sym} (SB) _) + // cond: symIsRO(sym) + // result: (MOVVconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) + for { + off := auxIntToInt32(v.AuxInt) + sym := auxToSym(v.Aux) + if v_0.Op != OpSB || !(symIsRO(sym)) { + break + } + v.reset(OpMIPS64MOVVconst) + v.AuxInt = int64ToAuxInt(int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))) + return true + } return false } func rewriteValueMIPS64_OpMIPS64MOVWUreg(v *Value) bool { @@ -4335,7 +4374,7 @@ func rewriteValueMIPS64_OpMIPS64MOVWload(v *Value) bool { } // match: (MOVWload [off] {sym} (SB) _) // cond: symIsRO(sym) - // result: (MOVVconst [int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))]) + // result: (MOVVconst [int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))]) for { off := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -4343,7 +4382,7 @@ func rewriteValueMIPS64_OpMIPS64MOVWload(v *Value) bool { break } v.reset(OpMIPS64MOVVconst) - v.AuxInt = int64ToAuxInt(int64(read32(sym, int64(off), config.ctxt.Arch.ByteOrder))) + v.AuxInt = int64ToAuxInt(int64(int32(read32(sym, int64(off), config.ctxt.Arch.ByteOrder)))) return true } return false diff --git a/src/cmd/compile/internal/ssa/rewriteRISCV64.go b/src/cmd/compile/internal/ssa/rewriteRISCV64.go index aa44ab311e92af..3a044b5c9d2602 100644 --- a/src/cmd/compile/internal/ssa/rewriteRISCV64.go +++ b/src/cmd/compile/internal/ssa/rewriteRISCV64.go @@ -4008,8 +4008,10 @@ func rewriteValueRISCV64_OpRISCV64FSUBS(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVBUload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVBUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBUload [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -4021,7 +4023,7 @@ func rewriteValueRISCV64_OpRISCV64MOVBUload(v *Value) bool { sym2 := auxToSym(v_0.Aux) base := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVBUload) @@ -4315,8 +4317,10 @@ func rewriteValueRISCV64_OpRISCV64MOVBUreg(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVBload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVBload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBload [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -4328,7 +4332,7 @@ func rewriteValueRISCV64_OpRISCV64MOVBload(v *Value) bool { sym2 := auxToSym(v_0.Aux) base := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVBload) @@ -4441,8 +4445,10 @@ func rewriteValueRISCV64_OpRISCV64MOVBstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVBstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVBstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -4455,7 +4461,7 @@ func rewriteValueRISCV64_OpRISCV64MOVBstore(v *Value) bool { base := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVBstore) @@ -4609,9 +4615,11 @@ func rewriteValueRISCV64_OpRISCV64MOVBstore(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVBstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (MOVBstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) + b := v.Block + config := b.Func.Config + // match: (MOVBstorezero [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) + // result: (MOVBstorezero [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -4620,20 +4628,20 @@ func rewriteValueRISCV64_OpRISCV64MOVBstorezero(v *Value) bool { } off2 := auxIntToInt32(v_0.AuxInt) sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVBstorezero) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } - // match: (MOVBstorezero [off1] {sym} (ADDI [off2] ptr) mem) + // match: (MOVBstorezero [off1] {sym} (ADDI [off2] base) mem) // cond: is32Bit(int64(off1)+off2) - // result: (MOVBstorezero [off1+int32(off2)] {sym} ptr mem) + // result: (MOVBstorezero [off1+int32(off2)] {sym} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -4641,7 +4649,7 @@ func rewriteValueRISCV64_OpRISCV64MOVBstorezero(v *Value) bool { break } off2 := auxIntToInt64(v_0.AuxInt) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 if !(is32Bit(int64(off1) + off2)) { break @@ -4649,7 +4657,7 @@ func rewriteValueRISCV64_OpRISCV64MOVBstorezero(v *Value) bool { v.reset(OpRISCV64MOVBstorezero) v.AuxInt = int32ToAuxInt(off1 + int32(off2)) v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } return false @@ -4657,8 +4665,10 @@ func rewriteValueRISCV64_OpRISCV64MOVBstorezero(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVDload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVDload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVDload [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -4670,7 +4680,7 @@ func rewriteValueRISCV64_OpRISCV64MOVDload(v *Value) bool { sym2 := auxToSym(v_0.Aux) base := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVDload) @@ -4737,8 +4747,10 @@ func rewriteValueRISCV64_OpRISCV64MOVDstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVDstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVDstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -4751,7 +4763,7 @@ func rewriteValueRISCV64_OpRISCV64MOVDstore(v *Value) bool { base := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVDstore) @@ -4803,9 +4815,11 @@ func rewriteValueRISCV64_OpRISCV64MOVDstore(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVDstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (MOVDstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) + b := v.Block + config := b.Func.Config + // match: (MOVDstorezero [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) + // result: (MOVDstorezero [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -4814,20 +4828,20 @@ func rewriteValueRISCV64_OpRISCV64MOVDstorezero(v *Value) bool { } off2 := auxIntToInt32(v_0.AuxInt) sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVDstorezero) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } - // match: (MOVDstorezero [off1] {sym} (ADDI [off2] ptr) mem) + // match: (MOVDstorezero [off1] {sym} (ADDI [off2] base) mem) // cond: is32Bit(int64(off1)+off2) - // result: (MOVDstorezero [off1+int32(off2)] {sym} ptr mem) + // result: (MOVDstorezero [off1+int32(off2)] {sym} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -4835,7 +4849,7 @@ func rewriteValueRISCV64_OpRISCV64MOVDstorezero(v *Value) bool { break } off2 := auxIntToInt64(v_0.AuxInt) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 if !(is32Bit(int64(off1) + off2)) { break @@ -4843,7 +4857,7 @@ func rewriteValueRISCV64_OpRISCV64MOVDstorezero(v *Value) bool { v.reset(OpRISCV64MOVDstorezero) v.AuxInt = int32ToAuxInt(off1 + int32(off2)) v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } return false @@ -4851,8 +4865,10 @@ func rewriteValueRISCV64_OpRISCV64MOVDstorezero(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVHUload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHUload [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -4864,7 +4880,7 @@ func rewriteValueRISCV64_OpRISCV64MOVHUload(v *Value) bool { sym2 := auxToSym(v_0.Aux) base := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVHUload) @@ -5015,8 +5031,10 @@ func rewriteValueRISCV64_OpRISCV64MOVHUreg(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVHload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHload [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -5028,7 +5046,7 @@ func rewriteValueRISCV64_OpRISCV64MOVHload(v *Value) bool { sym2 := auxToSym(v_0.Aux) base := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVHload) @@ -5185,8 +5203,10 @@ func rewriteValueRISCV64_OpRISCV64MOVHstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVHstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVHstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -5199,7 +5219,7 @@ func rewriteValueRISCV64_OpRISCV64MOVHstore(v *Value) bool { base := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVHstore) @@ -5319,9 +5339,11 @@ func rewriteValueRISCV64_OpRISCV64MOVHstore(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVHstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (MOVHstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) + b := v.Block + config := b.Func.Config + // match: (MOVHstorezero [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) + // result: (MOVHstorezero [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -5330,20 +5352,20 @@ func rewriteValueRISCV64_OpRISCV64MOVHstorezero(v *Value) bool { } off2 := auxIntToInt32(v_0.AuxInt) sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVHstorezero) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } - // match: (MOVHstorezero [off1] {sym} (ADDI [off2] ptr) mem) + // match: (MOVHstorezero [off1] {sym} (ADDI [off2] base) mem) // cond: is32Bit(int64(off1)+off2) - // result: (MOVHstorezero [off1+int32(off2)] {sym} ptr mem) + // result: (MOVHstorezero [off1+int32(off2)] {sym} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -5351,7 +5373,7 @@ func rewriteValueRISCV64_OpRISCV64MOVHstorezero(v *Value) bool { break } off2 := auxIntToInt64(v_0.AuxInt) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 if !(is32Bit(int64(off1) + off2)) { break @@ -5359,7 +5381,7 @@ func rewriteValueRISCV64_OpRISCV64MOVHstorezero(v *Value) bool { v.reset(OpRISCV64MOVHstorezero) v.AuxInt = int32ToAuxInt(off1 + int32(off2)) v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } return false @@ -5367,8 +5389,10 @@ func rewriteValueRISCV64_OpRISCV64MOVHstorezero(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVWUload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWUload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWUload [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -5380,7 +5404,7 @@ func rewriteValueRISCV64_OpRISCV64MOVWUload(v *Value) bool { sym2 := auxToSym(v_0.Aux) base := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVWUload) @@ -5555,8 +5579,10 @@ func rewriteValueRISCV64_OpRISCV64MOVWUreg(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVWload(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWload [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWload [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -5568,7 +5594,7 @@ func rewriteValueRISCV64_OpRISCV64MOVWload(v *Value) bool { sym2 := auxToSym(v_0.Aux) base := v_0.Args[0] mem := v_1 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVWload) @@ -5879,8 +5905,10 @@ func rewriteValueRISCV64_OpRISCV64MOVWstore(v *Value) bool { v_2 := v.Args[2] v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (MOVWstore [off1] {sym1} (MOVaddr [off2] {sym2} base) val mem) - // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) + // cond: is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) // result: (MOVWstore [off1+off2] {mergeSym(sym1,sym2)} base val mem) for { off1 := auxIntToInt32(v.AuxInt) @@ -5893,7 +5921,7 @@ func rewriteValueRISCV64_OpRISCV64MOVWstore(v *Value) bool { base := v_0.Args[0] val := v_1 mem := v_2 - if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2)) { + if !(is32Bit(int64(off1)+int64(off2)) && canMergeSym(sym1, sym2) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVWstore) @@ -5979,9 +6007,11 @@ func rewriteValueRISCV64_OpRISCV64MOVWstore(v *Value) bool { func rewriteValueRISCV64_OpRISCV64MOVWstorezero(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] - // match: (MOVWstorezero [off1] {sym1} (MOVaddr [off2] {sym2} ptr) mem) - // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) - // result: (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} ptr mem) + b := v.Block + config := b.Func.Config + // match: (MOVWstorezero [off1] {sym1} (MOVaddr [off2] {sym2} base) mem) + // cond: canMergeSym(sym1,sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink) + // result: (MOVWstorezero [off1+off2] {mergeSym(sym1,sym2)} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym1 := auxToSym(v.Aux) @@ -5990,20 +6020,20 @@ func rewriteValueRISCV64_OpRISCV64MOVWstorezero(v *Value) bool { } off2 := auxIntToInt32(v_0.AuxInt) sym2 := auxToSym(v_0.Aux) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 - if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2))) { + if !(canMergeSym(sym1, sym2) && is32Bit(int64(off1)+int64(off2)) && (base.Op != OpSB || !config.ctxt.Flag_dynlink)) { break } v.reset(OpRISCV64MOVWstorezero) v.AuxInt = int32ToAuxInt(off1 + off2) v.Aux = symToAux(mergeSym(sym1, sym2)) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } - // match: (MOVWstorezero [off1] {sym} (ADDI [off2] ptr) mem) + // match: (MOVWstorezero [off1] {sym} (ADDI [off2] base) mem) // cond: is32Bit(int64(off1)+off2) - // result: (MOVWstorezero [off1+int32(off2)] {sym} ptr mem) + // result: (MOVWstorezero [off1+int32(off2)] {sym} base mem) for { off1 := auxIntToInt32(v.AuxInt) sym := auxToSym(v.Aux) @@ -6011,7 +6041,7 @@ func rewriteValueRISCV64_OpRISCV64MOVWstorezero(v *Value) bool { break } off2 := auxIntToInt64(v_0.AuxInt) - ptr := v_0.Args[0] + base := v_0.Args[0] mem := v_1 if !(is32Bit(int64(off1) + off2)) { break @@ -6019,7 +6049,7 @@ func rewriteValueRISCV64_OpRISCV64MOVWstorezero(v *Value) bool { v.reset(OpRISCV64MOVWstorezero) v.AuxInt = int32ToAuxInt(off1 + int32(off2)) v.Aux = symToAux(sym) - v.AddArg2(ptr, mem) + v.AddArg2(base, mem) return true } return false diff --git a/src/cmd/compile/internal/ssa/rewriteWasm.go b/src/cmd/compile/internal/ssa/rewriteWasm.go index 6f83aea13afc5a..e0d753185f6649 100644 --- a/src/cmd/compile/internal/ssa/rewriteWasm.go +++ b/src/cmd/compile/internal/ssa/rewriteWasm.go @@ -3899,6 +3899,8 @@ func rewriteValueWasm_OpWasmI64Load(v *Value) bool { func rewriteValueWasm_OpWasmI64Load16S(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (I64Load16S [off] (I64AddConst [off2] ptr) mem) // cond: isU32Bit(off+off2) // result: (I64Load16S [off+off2] ptr mem) @@ -3918,6 +3920,24 @@ func rewriteValueWasm_OpWasmI64Load16S(v *Value) bool { v.AddArg2(ptr, mem) return true } + // match: (I64Load16S [off] (LoweredAddr {sym} [off2] (SB)) _) + // cond: symIsRO(sym) && isU32Bit(off+int64(off2)) + // result: (I64Const [int64(int16(read16(sym, off+int64(off2), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt64(v.AuxInt) + if v_0.Op != OpWasmLoweredAddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym := auxToSym(v_0.Aux) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpSB || !(symIsRO(sym) && isU32Bit(off+int64(off2))) { + break + } + v.reset(OpWasmI64Const) + v.AuxInt = int64ToAuxInt(int64(int16(read16(sym, off+int64(off2), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValueWasm_OpWasmI64Load16U(v *Value) bool { @@ -3967,6 +3987,8 @@ func rewriteValueWasm_OpWasmI64Load16U(v *Value) bool { func rewriteValueWasm_OpWasmI64Load32S(v *Value) bool { v_1 := v.Args[1] v_0 := v.Args[0] + b := v.Block + config := b.Func.Config // match: (I64Load32S [off] (I64AddConst [off2] ptr) mem) // cond: isU32Bit(off+off2) // result: (I64Load32S [off+off2] ptr mem) @@ -3986,6 +4008,24 @@ func rewriteValueWasm_OpWasmI64Load32S(v *Value) bool { v.AddArg2(ptr, mem) return true } + // match: (I64Load32S [off] (LoweredAddr {sym} [off2] (SB)) _) + // cond: symIsRO(sym) && isU32Bit(off+int64(off2)) + // result: (I64Const [int64(int32(read32(sym, off+int64(off2), config.ctxt.Arch.ByteOrder)))]) + for { + off := auxIntToInt64(v.AuxInt) + if v_0.Op != OpWasmLoweredAddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym := auxToSym(v_0.Aux) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpSB || !(symIsRO(sym) && isU32Bit(off+int64(off2))) { + break + } + v.reset(OpWasmI64Const) + v.AuxInt = int64ToAuxInt(int64(int32(read32(sym, off+int64(off2), config.ctxt.Arch.ByteOrder)))) + return true + } return false } func rewriteValueWasm_OpWasmI64Load32U(v *Value) bool { @@ -4054,6 +4094,24 @@ func rewriteValueWasm_OpWasmI64Load8S(v *Value) bool { v.AddArg2(ptr, mem) return true } + // match: (I64Load8S [off] (LoweredAddr {sym} [off2] (SB)) _) + // cond: symIsRO(sym) && isU32Bit(off+int64(off2)) + // result: (I64Const [int64(int8(read8(sym, off+int64(off2))))]) + for { + off := auxIntToInt64(v.AuxInt) + if v_0.Op != OpWasmLoweredAddr { + break + } + off2 := auxIntToInt32(v_0.AuxInt) + sym := auxToSym(v_0.Aux) + v_0_0 := v_0.Args[0] + if v_0_0.Op != OpSB || !(symIsRO(sym) && isU32Bit(off+int64(off2))) { + break + } + v.reset(OpWasmI64Const) + v.AuxInt = int64ToAuxInt(int64(int8(read8(sym, off+int64(off2))))) + return true + } return false } func rewriteValueWasm_OpWasmI64Load8U(v *Value) bool { diff --git a/src/cmd/compile/internal/ssa/rewrite_test.go b/src/cmd/compile/internal/ssa/rewrite_test.go index 357fe1183fad56..92e9d3fd5b9ca5 100644 --- a/src/cmd/compile/internal/ssa/rewrite_test.go +++ b/src/cmd/compile/internal/ssa/rewrite_test.go @@ -4,7 +4,12 @@ package ssa -import "testing" +import ( + "cmd/compile/internal/rttype" + "reflect" + "testing" + "unsafe" +) // We generate memmove for copy(x[1:], x[:]), however we may change it to OpMove, // because size is known. Check that OpMove is alias-safe, or we did call memmove. @@ -218,3 +223,65 @@ func TestMergePPC64AndSrwi(t *testing.T) { } } } + +func TestDisjointTypes(t *testing.T) { + tests := []struct { + v1, v2 any // two pointers to some types + expected bool + }{ + {new(int8), new(int8), false}, + {new(int8), new(float32), false}, + {new(int8), new(*int8), true}, + {new(*int8), new(*float32), false}, + {new(*int8), new(chan<- int8), false}, + {new(**int8), new(*int8), false}, + {new(***int8), new(**int8), false}, + {new(int8), new(chan<- int8), true}, + {new(int), unsafe.Pointer(nil), false}, + {new(byte), new(string), false}, + {new(int), new(string), false}, + {new(*int8), new(struct{ a, b int }), true}, + {new(*int8), new(struct { + a *int + b int + }), false}, + {new(*int8), new(struct { + a int + b *int + }), false}, // with more precise analysis it should be true + {new(*byte), new(string), false}, + {new(int), new(struct { + a int + b *int + }), false}, + {new(float64), new(complex128), false}, + {new(*byte), new([]byte), false}, + {new(int), new([]byte), false}, + {new(int), new([2]*byte), false}, // with more recise analysis it should be true + {new([2]int), new(*byte), true}, + } + for _, tst := range tests { + t1 := rttype.FromReflect(reflect.TypeOf(tst.v1)) + t2 := rttype.FromReflect(reflect.TypeOf(tst.v2)) + result := disjointTypes(t1, t2) + if result != tst.expected { + t.Errorf("disjointTypes(%s, %s) got %t expected %t", t1.String(), t2.String(), result, tst.expected) + } + } +} + +//go:noinline +func foo(p1 *int64, p2 *float64) int64 { + *p1 = 10 + *p2 = 0 // disjointTypes shouldn't consider this and preceding stores as non-aliasing + return *p1 +} + +func TestDisjointTypesRun(t *testing.T) { + f := float64(0) + i := (*int64)(unsafe.Pointer(&f)) + r := foo(i, &f) + if r != 0 { + t.Errorf("disjointTypes gives an incorrect answer that leads to an incorrect optimization.") + } +} diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 4cb287c9b7a0ed..d0b6e0b1001d04 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -18194,6 +18194,40 @@ func rewriteValuegeneric_OpMul16(v *Value) bool { } break } + // match: (Mul16 (Const16 [c]) (Add16 (Const16 [d]) x)) + // result: (Add16 (Const16 [c*d]) (Mul16 (Const16 [c]) x)) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst16 { + continue + } + t := v_0.Type + c := auxIntToInt16(v_0.AuxInt) + if v_1.Op != OpAdd16 || v_1.Type != t { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst16 || v_1_0.Type != t { + continue + } + d := auxIntToInt16(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpAdd16) + v0 := b.NewValue0(v.Pos, OpConst16, t) + v0.AuxInt = int16ToAuxInt(c * d) + v1 := b.NewValue0(v.Pos, OpMul16, t) + v2 := b.NewValue0(v.Pos, OpConst16, t) + v2.AuxInt = int16ToAuxInt(c) + v1.AddArg2(v2, x) + v.AddArg2(v0, v1) + return true + } + } + break + } // match: (Mul16 (Const16 [0]) _) // result: (Const16 [0]) for { @@ -18917,6 +18951,40 @@ func rewriteValuegeneric_OpMul8(v *Value) bool { } break } + // match: (Mul8 (Const8 [c]) (Add8 (Const8 [d]) x)) + // result: (Add8 (Const8 [c*d]) (Mul8 (Const8 [c]) x)) + for { + for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { + if v_0.Op != OpConst8 { + continue + } + t := v_0.Type + c := auxIntToInt8(v_0.AuxInt) + if v_1.Op != OpAdd8 || v_1.Type != t { + continue + } + _ = v_1.Args[1] + v_1_0 := v_1.Args[0] + v_1_1 := v_1.Args[1] + for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 { + if v_1_0.Op != OpConst8 || v_1_0.Type != t { + continue + } + d := auxIntToInt8(v_1_0.AuxInt) + x := v_1_1 + v.reset(OpAdd8) + v0 := b.NewValue0(v.Pos, OpConst8, t) + v0.AuxInt = int8ToAuxInt(c * d) + v1 := b.NewValue0(v.Pos, OpMul8, t) + v2 := b.NewValue0(v.Pos, OpConst8, t) + v2.AuxInt = int8ToAuxInt(c) + v1.AddArg2(v2, x) + v.AddArg2(v0, v1) + return true + } + } + break + } // match: (Mul8 (Const8 [0]) _) // result: (Const8 [0]) for { @@ -20610,6 +20678,17 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool { v.copyOf(ptr) return true } + // match: (NilCheck ptr:(LocalAddr _ _) _) + // cond: warnRule(fe.Debug_checknil(), v, "removed nil check") + // result: ptr + for { + ptr := v_0 + if ptr.Op != OpLocalAddr || !(warnRule(fe.Debug_checknil(), v, "removed nil check")) { + break + } + v.copyOf(ptr) + return true + } // match: (NilCheck ptr:(NilCheck _ _) _ ) // result: ptr for { @@ -30229,6 +30308,84 @@ func rewriteValuegeneric_OpStaticLECall(v *Value) bool { v.AddArg2(v0, mem) return true } + // match: (StaticLECall {f} typ_ x y mem) + // cond: isSameCall(f, "runtime.efaceeq") && isDirectType(typ_) && clobber(v) + // result: (MakeResult (EqPtr x y) mem) + for { + if len(v.Args) != 4 { + break + } + f := auxToCall(v.Aux) + mem := v.Args[3] + typ_ := v.Args[0] + x := v.Args[1] + y := v.Args[2] + if !(isSameCall(f, "runtime.efaceeq") && isDirectType(typ_) && clobber(v)) { + break + } + v.reset(OpMakeResult) + v0 := b.NewValue0(v.Pos, OpEqPtr, typ.Bool) + v0.AddArg2(x, y) + v.AddArg2(v0, mem) + return true + } + // match: (StaticLECall {f} itab x y mem) + // cond: isSameCall(f, "runtime.ifaceeq") && isDirectIface(itab) && clobber(v) + // result: (MakeResult (EqPtr x y) mem) + for { + if len(v.Args) != 4 { + break + } + f := auxToCall(v.Aux) + mem := v.Args[3] + itab := v.Args[0] + x := v.Args[1] + y := v.Args[2] + if !(isSameCall(f, "runtime.ifaceeq") && isDirectIface(itab) && clobber(v)) { + break + } + v.reset(OpMakeResult) + v0 := b.NewValue0(v.Pos, OpEqPtr, typ.Bool) + v0.AddArg2(x, y) + v.AddArg2(v0, mem) + return true + } + // match: (StaticLECall {f} [argsize] typ_ map_ key:(SelectN [0] sbts:(StaticLECall {g} _ ptr len mem)) m:(SelectN [1] sbts)) + // cond: (isSameCall(f, "runtime.mapaccess1_faststr") || isSameCall(f, "runtime.mapaccess2_faststr") || isSameCall(f, "runtime.mapdelete_faststr")) && isSameCall(g, "runtime.slicebytetostring") && key.Uses == 1 && sbts.Uses == 2 && resetCopy(m, mem) && clobber(sbts) && clobber(key) + // result: (StaticLECall {f} [argsize] typ_ map_ (StringMake ptr len) mem) + for { + if len(v.Args) != 4 { + break + } + argsize := auxIntToInt32(v.AuxInt) + f := auxToCall(v.Aux) + _ = v.Args[3] + typ_ := v.Args[0] + map_ := v.Args[1] + key := v.Args[2] + if key.Op != OpSelectN || auxIntToInt64(key.AuxInt) != 0 { + break + } + sbts := key.Args[0] + if sbts.Op != OpStaticLECall || len(sbts.Args) != 4 { + break + } + g := auxToCall(sbts.Aux) + mem := sbts.Args[3] + ptr := sbts.Args[1] + len := sbts.Args[2] + m := v.Args[3] + if m.Op != OpSelectN || auxIntToInt64(m.AuxInt) != 1 || sbts != m.Args[0] || !((isSameCall(f, "runtime.mapaccess1_faststr") || isSameCall(f, "runtime.mapaccess2_faststr") || isSameCall(f, "runtime.mapdelete_faststr")) && isSameCall(g, "runtime.slicebytetostring") && key.Uses == 1 && sbts.Uses == 2 && resetCopy(m, mem) && clobber(sbts) && clobber(key)) { + break + } + v.reset(OpStaticLECall) + v.AuxInt = int32ToAuxInt(argsize) + v.Aux = callToAux(f) + v0 := b.NewValue0(v.Pos, OpStringMake, typ.String) + v0.AddArg2(ptr, len) + v.AddArg4(typ_, map_, v0, mem) + return true + } return false } func rewriteValuegeneric_OpStore(v *Value) bool { diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go index 1caccb7c18d3c4..bf04f1b5c228d5 100644 --- a/src/cmd/compile/internal/ssa/writebarrier.go +++ b/src/cmd/compile/internal/ssa/writebarrier.go @@ -252,6 +252,7 @@ func writebarrier(f *Func) { var start, end int var nonPtrStores int values := b.Values + hasMove := false FindSeq: for i := len(values) - 1; i >= 0; i-- { w := values[i] @@ -263,6 +264,9 @@ func writebarrier(f *Func) { end = i + 1 } nonPtrStores = 0 + if w.Op == OpMoveWB { + hasMove = true + } case OpVarDef, OpVarLive: continue case OpStore: @@ -273,6 +277,17 @@ func writebarrier(f *Func) { if nonPtrStores > 2 { break FindSeq } + if hasMove { + // We need to ensure that this store happens + // before we issue a wbMove, as the wbMove might + // use the result of this store as its source. + // Even though this store is not write-barrier + // eligible, it might nevertheless be the store + // of a pointer to the stack, which is then the + // source of the move. + // See issue 71228. + break FindSeq + } default: if last == nil { continue @@ -361,21 +376,6 @@ func writebarrier(f *Func) { // For each write barrier store, append write barrier code to bThen. memThen := mem - var curCall *Value - var curPtr *Value - addEntry := func(pos src.XPos, v *Value) { - if curCall == nil || curCall.AuxInt == maxEntries { - t := types.NewTuple(types.Types[types.TUINTPTR].PtrTo(), types.TypeMem) - curCall = bThen.NewValue1(pos, OpWB, t, memThen) - curPtr = bThen.NewValue1(pos, OpSelect0, types.Types[types.TUINTPTR].PtrTo(), curCall) - memThen = bThen.NewValue1(pos, OpSelect1, types.TypeMem, curCall) - } - // Store value in write buffer - num := curCall.AuxInt - curCall.AuxInt = num + 1 - wbuf := bThen.NewValue1I(pos, OpOffPtr, types.Types[types.TUINTPTR].PtrTo(), num*f.Config.PtrSize, curPtr) - memThen = bThen.NewValue3A(pos, OpStore, types.TypeMem, types.Types[types.TUINTPTR], wbuf, v, memThen) - } // Note: we can issue the write barrier code in any order. In particular, // it doesn't matter if they are in a different order *even if* they end @@ -395,6 +395,38 @@ func writebarrier(f *Func) { dsts := sset2 dsts.clear() + // Buffer up entries that we need to put in the write barrier buffer. + type write struct { + ptr *Value // value to put in write barrier buffer + pos src.XPos // location to use for the write + } + var writeStore [maxEntries]write + writes := writeStore[:0] + + flush := func() { + if len(writes) == 0 { + return + } + // Issue a call to get a write barrier buffer. + t := types.NewTuple(types.Types[types.TUINTPTR].PtrTo(), types.TypeMem) + call := bThen.NewValue1I(pos, OpWB, t, int64(len(writes)), memThen) + curPtr := bThen.NewValue1(pos, OpSelect0, types.Types[types.TUINTPTR].PtrTo(), call) + memThen = bThen.NewValue1(pos, OpSelect1, types.TypeMem, call) + // Write each pending pointer to a slot in the buffer. + for i, write := range writes { + wbuf := bThen.NewValue1I(write.pos, OpOffPtr, types.Types[types.TUINTPTR].PtrTo(), int64(i)*f.Config.PtrSize, curPtr) + memThen = bThen.NewValue3A(write.pos, OpStore, types.TypeMem, types.Types[types.TUINTPTR], wbuf, write.ptr, memThen) + } + writes = writes[:0] + } + addEntry := func(pos src.XPos, ptr *Value) { + writes = append(writes, write{ptr: ptr, pos: pos}) + if len(writes) == maxEntries { + flush() + } + } + + // Find all the pointers we need to write to the buffer. for _, w := range stores { if w.Op != OpStoreWB { continue @@ -422,7 +454,9 @@ func writebarrier(f *Func) { f.fe.Func().SetWBPos(pos) nWBops-- } + flush() + // Now do the rare cases, Zeros and Moves. for _, w := range stores { pos := w.Pos switch w.Op { diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go index dc9b508c01ed12..6e8a8b9cc86fb2 100644 --- a/src/cmd/compile/internal/ssagen/ssa.go +++ b/src/cmd/compile/internal/ssagen/ssa.go @@ -5452,12 +5452,15 @@ func (s *state) referenceTypeBuiltin(n *ir.UnaryExpr, x *ssa.Value) *ssa.Value { if n.X.Type().IsChan() && n.Op() == ir.OCAP { s.Fatalf("cannot inline cap(chan)") // must use runtime.chancap now } + if n.X.Type().IsMap() && n.Op() == ir.OCAP { + s.Fatalf("cannot inline cap(map)") // cap(map) does not exist + } // if n == nil { // return 0 // } else { - // // len - // return *((*int)n) - // // cap + // // len, the actual loadType depends + // return int(*((*loadType)n)) + // // cap (chan only, not used for now) // return *(((*int)n)+1) // } lenType := n.Type() @@ -5485,7 +5488,9 @@ func (s *state) referenceTypeBuiltin(n *ir.UnaryExpr, x *ssa.Value) *ssa.Value { case ir.OLEN: if buildcfg.Experiment.SwissMap && n.X.Type().IsMap() { // length is stored in the first word. - s.vars[n] = s.load(lenType, x) + loadType := reflectdata.SwissMapType().Field(0).Type // uint64 + load := s.load(loadType, x) + s.vars[n] = s.conv(nil, load, loadType, lenType) // integer conversion doesn't need Node } else { // length is stored in the first word for map/chan s.vars[n] = s.load(lenType, x) @@ -6330,7 +6335,10 @@ func genssa(f *ssa.Func, pp *objw.Progs) { e := f.Frontend().(*ssafn) - s.livenessMap, s.partLiveArgs = liveness.Compute(e.curfn, f, e.stkptrsize, pp) + gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name] + + var lv *liveness.Liveness + s.livenessMap, s.partLiveArgs, lv = liveness.Compute(e.curfn, f, e.stkptrsize, pp, gatherPrintInfo) emitArgInfo(e, f, pp) argLiveBlockMap, argLiveValueMap := liveness.ArgLiveness(e.curfn, f, pp) @@ -6353,7 +6361,6 @@ func genssa(f *ssa.Func, pp *objw.Progs) { var progToValue map[*obj.Prog]*ssa.Value var progToBlock map[*obj.Prog]*ssa.Block var valueToProgAfter []*obj.Prog // The first Prog following computation of a value v; v is visible at this point. - gatherPrintInfo := f.PrintOrHtmlSSA || ssa.GenssaDump[f.Name] if gatherPrintInfo { progToValue = make(map[*obj.Prog]*ssa.Value, f.NumValues()) progToBlock = make(map[*obj.Prog]*ssa.Block, f.NumBlocks()) @@ -6761,6 +6768,14 @@ func genssa(f *ssa.Func, pp *objw.Progs) { buf.WriteString("") buf.WriteString("

    ") filename := "" + + liveness := lv.Format(nil) + if liveness != "" { + buf.WriteString("
    ") + buf.WriteString(html.EscapeString("# " + liveness)) + buf.WriteString("
    ") + } + for p := s.pp.Text; p != nil; p = p.Link { // Don't spam every line with the file name, which is often huge. // Only print changes, and "unknown" is not a change. @@ -6773,6 +6788,19 @@ func genssa(f *ssa.Func, pp *objw.Progs) { buf.WriteString("
    ") if v, ok := progToValue[p]; ok { + + // Prefix calls with their liveness, if any + if p.As != obj.APCDATA { + if liveness := lv.Format(v); liveness != "" { + // Steal this line, and restart a line + buf.WriteString("
    ") + buf.WriteString(html.EscapeString("# " + liveness)) + buf.WriteString("
    ") + // restarting a line + buf.WriteString("
    ") + } + } + buf.WriteString(v.HTML()) } else if b, ok := progToBlock[p]; ok { buf.WriteString("" + b.HTML() + "") diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index 14a737c414988e..82786859435b97 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -650,7 +650,7 @@ func (p *parser) typeDecl(group *Group) Decl { // d.Name "[" pname ... // d.Name "[" pname ptype ... // d.Name "[" pname ptype "," ... - d.TParamList = p.paramList(pname, ptype, _Rbrack, true) // ptype may be nil + d.TParamList = p.paramList(pname, ptype, _Rbrack, true, false) // ptype may be nil d.Alias = p.gotAssign() d.Type = p.typeOrNil() } else { @@ -800,7 +800,7 @@ func (p *parser) funcDeclOrNil() *FuncDecl { var context string if p.got(_Lparen) { context = "method" - rcvr := p.paramList(nil, nil, _Rparen, false) + rcvr := p.paramList(nil, nil, _Rparen, false, false) switch len(rcvr) { case 0: p.error("method has no receiver") @@ -1469,12 +1469,12 @@ func (p *parser) funcType(context string) ([]*Field, *FuncType) { p.syntaxError("empty type parameter list") p.next() } else { - tparamList = p.paramList(nil, nil, _Rbrack, true) + tparamList = p.paramList(nil, nil, _Rbrack, true, false) } } p.want(_Lparen) - typ.ParamList = p.paramList(nil, nil, _Rparen, false) + typ.ParamList = p.paramList(nil, nil, _Rparen, false, true) typ.ResultList = p.funcResult() return tparamList, typ @@ -1582,7 +1582,7 @@ func (p *parser) funcResult() []*Field { } if p.got(_Lparen) { - return p.paramList(nil, nil, _Rparen, false) + return p.paramList(nil, nil, _Rparen, false, false) } pos := p.pos() @@ -1793,7 +1793,7 @@ func (p *parser) methodDecl() *Field { // A type argument list looks like a parameter list with only // types. Parse a parameter list and decide afterwards. - list := p.paramList(nil, nil, _Rbrack, false) + list := p.paramList(nil, nil, _Rbrack, false, false) if len(list) == 0 { // The type parameter list is not [] but we got nothing // due to other errors (reported by paramList). Treat @@ -1962,10 +1962,11 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field { p.next() t.Elem = p.typeOrNil() if t.Elem == nil { - t.Elem = p.badExpr() + f.Type = p.badExpr() p.syntaxError("... is missing type") + } else { + f.Type = t } - f.Type = t return f } @@ -1995,7 +1996,7 @@ func (p *parser) paramDeclOrNil(name *Name, follow token) *Field { // If name != nil, it is the first name after "(" or "[". // If typ != nil, name must be != nil, and (name, typ) is the first field in the list. // In the result list, either all fields have a name, or no field has a name. -func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) (list []*Field) { +func (p *parser) paramList(name *Name, typ Expr, close token, requireNames, dddok bool) (list []*Field) { if trace { defer p.trace("paramList")() } @@ -2109,6 +2110,23 @@ func (p *parser) paramList(name *Name, typ Expr, close token, requireNames bool) } } + // check use of ... + first := true // only report first occurrence + for i, f := range list { + if t, _ := f.Type.(*DotsType); t != nil && (!dddok || i+1 < len(list)) { + if first { + first = false + if dddok { + p.errorAt(t.pos, "can only use ... with final parameter") + } else { + p.errorAt(t.pos, "invalid use of ...") + } + } + // use T instead of invalid ...T + f.Type = t.Elem + } + } + return } diff --git a/src/cmd/compile/internal/syntax/testdata/issue70974.go b/src/cmd/compile/internal/syntax/testdata/issue70974.go new file mode 100644 index 00000000000000..ebc69eda950254 --- /dev/null +++ b/src/cmd/compile/internal/syntax/testdata/issue70974.go @@ -0,0 +1,17 @@ +// Copyright 2025 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. + +package p + +func _() { +M: +L: + for range 0 { + break L + break /* ERROR invalid break label M */ M + } + for range 0 { + break /* ERROR invalid break label L */ L + } +} diff --git a/src/cmd/compile/internal/test/inl_test.go b/src/cmd/compile/internal/test/inl_test.go index 9a1a8bb1059ca6..f1f6c34bfc8f9a 100644 --- a/src/cmd/compile/internal/test/inl_test.go +++ b/src/cmd/compile/internal/test/inl_test.go @@ -375,10 +375,6 @@ func TestIssue56044(t *testing.T) { if testing.Short() { t.Skipf("skipping test: too long for short mode") } - if !goexperiment.CoverageRedesign { - t.Skipf("skipping new coverage tests (experiment not enabled)") - } - testenv.MustHaveGoBuild(t) modes := []string{"-covermode=set", "-covermode=atomic"} diff --git a/src/cmd/compile/internal/typecheck/_builtin/runtime.go b/src/cmd/compile/internal/typecheck/_builtin/runtime.go index 9a83911487830a..cf07f31e31b83e 100644 --- a/src/cmd/compile/internal/typecheck/_builtin/runtime.go +++ b/src/cmd/compile/internal/typecheck/_builtin/runtime.go @@ -152,12 +152,14 @@ func mapassign_fast32ptr(mapType *byte, hmap map[any]any, key unsafe.Pointer) (v func mapassign_fast64(mapType *byte, hmap map[any]any, key uint64) (val *any) func mapassign_fast64ptr(mapType *byte, hmap map[any]any, key unsafe.Pointer) (val *any) func mapassign_faststr(mapType *byte, hmap map[any]any, key string) (val *any) -func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) +func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) // old maps +func mapIterStart(mapType *byte, hmap map[any]any, hiter *any) // swiss maps func mapdelete(mapType *byte, hmap map[any]any, key *any) func mapdelete_fast32(mapType *byte, hmap map[any]any, key uint32) func mapdelete_fast64(mapType *byte, hmap map[any]any, key uint64) func mapdelete_faststr(mapType *byte, hmap map[any]any, key string) -func mapiternext(hiter *any) +func mapiternext(hiter *any) // old maps +func mapIterNext(hiter *any) // swiss maps func mapclear(mapType *byte, hmap map[any]any) // *byte is really *runtime.Type diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go index 6860d78b2e4024..be08d0b40329a1 100644 --- a/src/cmd/compile/internal/typecheck/builtin.go +++ b/src/cmd/compile/internal/typecheck/builtin.go @@ -131,11 +131,13 @@ var runtimeDecls = [...]struct { {"mapassign_fast64ptr", funcTag, 96}, {"mapassign_faststr", funcTag, 89}, {"mapiterinit", funcTag, 97}, + {"mapIterStart", funcTag, 97}, {"mapdelete", funcTag, 97}, {"mapdelete_fast32", funcTag, 98}, {"mapdelete_fast64", funcTag, 99}, {"mapdelete_faststr", funcTag, 100}, {"mapiternext", funcTag, 101}, + {"mapIterNext", funcTag, 101}, {"mapclear", funcTag, 102}, {"makechan64", funcTag, 104}, {"makechan", funcTag, 105}, diff --git a/src/cmd/compile/internal/types/fmt.go b/src/cmd/compile/internal/types/fmt.go index 0dba510ac44e20..139defafe20b4c 100644 --- a/src/cmd/compile/internal/types/fmt.go +++ b/src/cmd/compile/internal/types/fmt.go @@ -646,7 +646,7 @@ func SplitVargenSuffix(name string) (base, suffix string) { func TypeHash(t *Type) uint32 { p := t.LinkString() - // Using 16 bytes hash is overkill, but reduces accidental collisions. - h := hash.Sum16([]byte(p)) + // Using a cryptographic hash is overkill but minimizes accidental collisions. + h := hash.Sum32([]byte(p)) return binary.LittleEndian.Uint32(h[:4]) } diff --git a/src/cmd/compile/internal/types2/README.md b/src/cmd/compile/internal/types2/README.md index 3d70cdbcf484dd..73253b49207aac 100644 --- a/src/cmd/compile/internal/types2/README.md +++ b/src/cmd/compile/internal/types2/README.md @@ -56,7 +56,7 @@ The tests are in: Tests are .go files annotated with `/* ERROR "msg" */` or `/* ERRORx "msg" */` comments (or the respective line comment form). For each such error comment, typechecking the respective file is expected to -report an error at the position of the syntactic token _immediately preceeding_ +report an error at the position of the syntactic token _immediately preceding_ the comment. For `ERROR`, the `"msg"` string must be a substring of the error message reported by the typechecker; diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go index 74c549076df090..49cc0e54ecf750 100644 --- a/src/cmd/compile/internal/types2/api.go +++ b/src/cmd/compile/internal/types2/api.go @@ -208,11 +208,19 @@ type Info struct { // // The Types map does not record the type of every identifier, // only those that appear where an arbitrary expression is - // permitted. For instance, the identifier f in a selector - // expression x.f is found only in the Selections map, the - // identifier z in a variable declaration 'var z int' is found - // only in the Defs map, and identifiers denoting packages in - // qualified identifiers are collected in the Uses map. + // permitted. For instance: + // - an identifier f in a selector expression x.f is found + // only in the Selections map; + // - an identifier z in a variable declaration 'var z int' + // is found only in the Defs map; + // - an identifier p denoting a package in a qualified + // identifier p.X is found only in the Uses map. + // + // Similarly, no type is recorded for the (synthetic) FuncType + // node in a FuncDecl.Type field, since there is no corresponding + // syntactic function type expression in the source in this case + // Instead, the function type is found in the Defs.map entry for + // the corresponding function declaration. Types map[syntax.Expr]TypeAndValue // If StoreTypesInSyntax is set, type information identical to diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go index ae2ab5f9849bc9..897c846d8ff98d 100644 --- a/src/cmd/compile/internal/types2/call.go +++ b/src/cmd/compile/internal/types2/call.go @@ -142,6 +142,9 @@ func (check *Checker) instantiateSignature(pos syntax.Pos, expr syntax.Expr, typ }() } + // For signatures, Checker.instance will always succeed because the type argument + // count is correct at this point (see assertion above); hence the type assertion + // to *Signature will always succeed. inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature) assert(inst.TypeParams().Len() == 0) // signature is not generic anymore check.recordInstance(expr, targs, inst) diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go index 2bf42d1c6f1494..a73e073ac3eeb1 100644 --- a/src/cmd/compile/internal/types2/expr.go +++ b/src/cmd/compile/internal/types2/expr.go @@ -148,26 +148,13 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) { return case syntax.Recv: - u := coreType(x.typ) - if u == nil { - check.errorf(x, InvalidReceive, invalidOp+"cannot receive from %s (no core type)", x) - x.mode = invalid + if elem := check.chanElem(x, x, true); elem != nil { + x.mode = commaok + x.typ = elem + check.hasCallOrRecv = true return } - ch, _ := u.(*Chan) - if ch == nil { - check.errorf(x, InvalidReceive, invalidOp+"cannot receive from non-channel %s", x) - x.mode = invalid - return - } - if ch.dir == SendOnly { - check.errorf(x, InvalidReceive, invalidOp+"cannot receive from send-only channel %s", x) - x.mode = invalid - return - } - x.mode = commaok - x.typ = ch.elem - check.hasCallOrRecv = true + x.mode = invalid return case syntax.Tilde: @@ -205,6 +192,62 @@ func (check *Checker) unary(x *operand, e *syntax.Operation) { // x.typ remains unchanged } +// chanElem returns the channel element type of x for a receive from x (recv == true) +// or send to x (recv == false) operation. If the operation is not valid, chanElem +// reports an error and returns nil. +func (check *Checker) chanElem(pos poser, x *operand, recv bool) Type { + var elem Type + var cause string + typeset(x.typ, func(t, u Type) bool { + if u == nil { + // Type set contains no explicit terms. + // It is either empty or contains all types (any) + cause = "no specific channel type" + return false + } + ch, _ := u.(*Chan) + if ch == nil { + cause = check.sprintf("non-channel %s", t) + return false + } + if recv && ch.dir == SendOnly { + cause = check.sprintf("send-only channel %s", t) + return false + } + if !recv && ch.dir == RecvOnly { + cause = check.sprintf("receive-only channel %s", t) + return false + } + if elem != nil && !Identical(elem, ch.elem) { + cause = check.sprintf("channels with different element types %s and %s", elem, ch.elem) + return false + } + elem = ch.elem + return true + }) + + if cause == "" { + return elem + } + + if recv { + if isTypeParam(x.typ) { + check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %s", x, cause) + } else { + // In this case, only the non-channel and send-only channel error are possible. + check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s %s", cause, x) + } + } else { + if isTypeParam(x.typ) { + check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: type set contains %s", x, cause) + } else { + // In this case, only the non-channel and receive-only channel error are possible. + check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s %s", cause, x) + } + } + return nil +} + func isShift(op syntax.Operator) bool { return op == syntax.Shl || op == syntax.Shr } @@ -1016,9 +1059,8 @@ func (check *Checker) exprInternal(T *target, x *operand, e syntax.Expr, hint Ty check.ident(x, e, nil, false) case *syntax.DotsType: - // dots are handled explicitly where they are legal - // (array composite literals and parameter lists) - check.error(e, BadDotDotDotSyntax, "invalid use of '...'") + // dots are handled explicitly where they are valid + check.error(e, InvalidSyntaxTree, "invalid use of ...") goto Error case *syntax.BasicLit: diff --git a/src/cmd/compile/internal/types2/initorder.go b/src/cmd/compile/internal/types2/initorder.go index ef2ad010a6d5fb..699bfca8bbc895 100644 --- a/src/cmd/compile/internal/types2/initorder.go +++ b/src/cmd/compile/internal/types2/initorder.go @@ -10,6 +10,7 @@ import ( "fmt" . "internal/types/errors" "slices" + "sort" ) // initOrder computes the Info.InitOrder for package variables. @@ -139,7 +140,16 @@ func findPath(objMap map[Object]*declInfo, from, to Object, seen map[Object]bool } seen[from] = true + // sort deps for deterministic result + var deps []Object for d := range objMap[from].deps { + deps = append(deps, d) + } + sort.Slice(deps, func(i, j int) bool { + return deps[i].order() < deps[j].order() + }) + + for _, d := range deps { if d == to { return []Object{d} } diff --git a/src/cmd/compile/internal/types2/instantiate.go b/src/cmd/compile/internal/types2/instantiate.go index e51cf18de67fd6..03c490a3866df2 100644 --- a/src/cmd/compile/internal/types2/instantiate.go +++ b/src/cmd/compile/internal/types2/instantiate.go @@ -74,7 +74,8 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e // instance instantiates the given original (generic) function or type with the // provided type arguments and returns the resulting instance. If an identical // instance exists already in the given contexts, it returns that instance, -// otherwise it creates a new one. +// otherwise it creates a new one. If there is an error (such as wrong number +// of type arguments), the result is Typ[Invalid]. // // If expanding is non-nil, it is the Named instance type currently being // expanded. If ctxt is non-nil, it is the context associated with the current @@ -133,9 +134,13 @@ func (check *Checker) instance(pos syntax.Pos, orig genericType, targs []Type, e assert(expanding == nil) // Alias instances cannot be reached from Named types } + // verify type parameter count (see go.dev/issue/71198 for a test case) tparams := orig.TypeParams() - // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here) - if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) { + if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) { + // TODO(gri) Consider returning a valid alias instance with invalid + // underlying (aliased) type to match behavior of *Named + // types. Then this function will never return an invalid + // result. return Typ[Invalid] } if tparams.Len() == 0 { diff --git a/src/cmd/compile/internal/types2/interface.go b/src/cmd/compile/internal/types2/interface.go index 4072098e05234d..67f5b98a835cd9 100644 --- a/src/cmd/compile/internal/types2/interface.go +++ b/src/cmd/compile/internal/types2/interface.go @@ -137,17 +137,19 @@ func (check *Checker) interfaceType(ityp *Interface, iface *syntax.InterfaceType name := f.Name.Value if name == "_" { check.error(f.Name, BlankIfaceMethod, "methods must have a unique non-blank name") - continue // ignore + continue // ignore method } - typ := check.typ(f.Type) - sig, _ := typ.(*Signature) - if sig == nil { - if isValid(typ) { - check.errorf(f.Type, InvalidSyntaxTree, "%s is not a method signature", typ) - } - continue // ignore + // Type-check method declaration. + // Note: Don't call check.typ(f.Type) as that would record + // the method incorrectly as a type expression in Info.Types. + ftyp, _ := f.Type.(*syntax.FuncType) + if ftyp == nil { + check.errorf(f.Type, InvalidSyntaxTree, "%s is not a method signature", f.Type) + continue // ignore method } + sig := new(Signature) + check.funcType(sig, nil, nil, ftyp) // use named receiver type if available (for better error messages) var recvTyp Type = ityp diff --git a/src/cmd/compile/internal/types2/labels.go b/src/cmd/compile/internal/types2/labels.go index e44b7c7f70f3cd..6a6f64b4b8a7b2 100644 --- a/src/cmd/compile/internal/types2/labels.go +++ b/src/cmd/compile/internal/types2/labels.go @@ -112,8 +112,8 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.Lab return varDeclPos.IsKnown() && slices.Contains(badJumps, jmp) } - var stmtBranches func(syntax.Stmt) - stmtBranches = func(s syntax.Stmt) { + var stmtBranches func(*syntax.LabeledStmt, syntax.Stmt) + stmtBranches = func(lstmt *syntax.LabeledStmt, s syntax.Stmt) { switch s := s.(type) { case *syntax.DeclStmt: for _, d := range s.DeclList { @@ -163,7 +163,7 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.Lab fwdJumps = fwdJumps[:i] lstmt = s } - stmtBranches(s.Stmt) + stmtBranches(lstmt, s.Stmt) case *syntax.BranchStmt: if s.Label == nil { @@ -232,9 +232,9 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.Lab fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, s.List)...) case *syntax.IfStmt: - stmtBranches(s.Then) + stmtBranches(lstmt, s.Then) if s.Else != nil { - stmtBranches(s.Else) + stmtBranches(lstmt, s.Else) } case *syntax.SwitchStmt: @@ -250,12 +250,12 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *syntax.Lab } case *syntax.ForStmt: - stmtBranches(s.Body) + stmtBranches(lstmt, s.Body) } } for _, s := range list { - stmtBranches(s) + stmtBranches(nil, s) } return fwdJumps diff --git a/src/cmd/compile/internal/types2/signature.go b/src/cmd/compile/internal/types2/signature.go index d3169630eaa0bd..622eb1383da000 100644 --- a/src/cmd/compile/internal/types2/signature.go +++ b/src/cmd/compile/internal/types2/signature.go @@ -174,7 +174,7 @@ func (check *Checker) collectRecv(rparam *syntax.Field, scopePos syntax.Pos) (*V } else { // If there are type parameters, rbase must denote a generic base type. // Important: rbase must be resolved before declaring any receiver type - // parameters (wich may have the same name, see below). + // parameters (which may have the same name, see below). var baseType *Named // nil if not valid var cause string if t := check.genericType(rbase, &cause); isValid(t) { @@ -344,7 +344,7 @@ func (check *Checker) collectParams(list []*syntax.Field, variadicOk bool) (name if variadicOk && i == len(list)-1 { variadic = true } else { - check.softErrorf(t, MisplacedDotDotDot, "can only use ... with final parameter in list") + check.error(t, InvalidSyntaxTree, "invalid use of ...") // ignore ... and continue } } diff --git a/src/cmd/compile/internal/types2/stmt.go b/src/cmd/compile/internal/types2/stmt.go index 2174aedf7f4f01..60955da4fc7a20 100644 --- a/src/cmd/compile/internal/types2/stmt.go +++ b/src/cmd/compile/internal/types2/stmt.go @@ -465,21 +465,9 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) { if ch.mode == invalid || val.mode == invalid { return } - u := coreType(ch.typ) - if u == nil { - check.errorf(s, InvalidSend, invalidOp+"cannot send to %s: no core type", &ch) - return - } - uch, _ := u.(*Chan) - if uch == nil { - check.errorf(s, InvalidSend, invalidOp+"cannot send to non-channel %s", &ch) - return + if elem := check.chanElem(s, &ch, false); elem != nil { + check.assignment(&val, elem, "send") } - if uch.dir == RecvOnly { - check.errorf(s, InvalidSend, invalidOp+"cannot send to receive-only channel %s", &ch) - return - } - check.assignment(&val, uch.elem, "send") case *syntax.AssignStmt: if s.Rhs == nil { @@ -1057,8 +1045,13 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca return bad("func must be func(yield func(...) bool): argument is not func") case cb.Params().Len() > 2: return bad("func must be func(yield func(...) bool): yield func has too many parameters") - case cb.Results().Len() != 1 || !isBoolean(cb.Results().At(0).Type()): - return bad("func must be func(yield func(...) bool): yield func does not return bool") + case cb.Results().Len() != 1 || !Identical(cb.Results().At(0).Type(), universeBool): + // see go.dev/issues/71131, go.dev/issues/71164 + if cb.Results().Len() == 1 && isBoolean(cb.Results().At(0).Type()) { + return bad("func must be func(yield func(...) bool): yield func returns user-defined boolean, not bool") + } else { + return bad("func must be func(yield func(...) bool): yield func does not return bool") + } } assert(cb.Recv() == nil) // determine key and value types, if any diff --git a/src/cmd/compile/internal/types2/testdata/local/issue71254.go b/src/cmd/compile/internal/types2/testdata/local/issue71254.go new file mode 100644 index 00000000000000..9cca9d5bc4dc6e --- /dev/null +++ b/src/cmd/compile/internal/types2/testdata/local/issue71254.go @@ -0,0 +1,14 @@ +// Copyright 2025 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. + +package p + +const ( + B /* ERROR "initialization cycle: B refers to itself" */ = A + B + A /* ERRORx "initialization cycle for A\\s+.*A refers to B\\s+.*B refers to A" */ = A + B + + C /* ERRORx "initialization cycle for C\\s+.*C refers to D\\s+.*D refers to C" */ = E + D + D /* ERRORx "initialization cycle for D\\s+.*D refers to C\\s+.*C refers to D" */ = E + C + E = D + C +) diff --git a/src/cmd/compile/internal/types2/testdata/manual.go b/src/cmd/compile/internal/types2/testdata/manual.go index d8f312f61d58c5..825ab50f92de49 100644 --- a/src/cmd/compile/internal/types2/testdata/manual.go +++ b/src/cmd/compile/internal/types2/testdata/manual.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Go Authors. All rights reserved. +// Copyright 2025 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. diff --git a/src/cmd/compile/internal/types2/typexpr.go b/src/cmd/compile/internal/types2/typexpr.go index d955654fc9af37..0964c53fe05815 100644 --- a/src/cmd/compile/internal/types2/typexpr.go +++ b/src/cmd/compile/internal/types2/typexpr.go @@ -321,10 +321,8 @@ func (check *Checker) typInternal(e0 syntax.Expr, def *TypeName) (T Type) { return typ case *syntax.DotsType: - // dots are handled explicitly where they are legal - // (array composite literals and parameter lists) - check.error(e, InvalidDotDotDot, "invalid use of '...'") - check.use(e.Elem) + // dots are handled explicitly where they are valid + check.error(e, InvalidSyntaxTree, "invalid use of ...") case *syntax.StructType: typ := new(Struct) @@ -423,11 +421,6 @@ func setDefType(def *TypeName, typ Type) { if def != nil { switch t := def.typ.(type) { case *Alias: - // t.fromRHS should always be set, either to an invalid type - // in the beginning, or to typ in certain cyclic declarations. - if t.fromRHS != Typ[Invalid] && t.fromRHS != typ { - panic(sprintf(nil, true, "t.fromRHS = %s, typ = %s\n", t.fromRHS, typ)) - } t.fromRHS = typ case *Basic: assert(t == Typ[Invalid]) @@ -475,9 +468,14 @@ func (check *Checker) instantiatedType(x syntax.Expr, xlist []syntax.Expr, def * } // create instance - // The instance is not generic anymore as it has type arguments, but it still - // satisfies the genericType interface because it has type parameters, too. - inst := check.instance(x.Pos(), gtyp, targs, nil, check.context()).(genericType) + // The instance is not generic anymore as it has type arguments, but unless + // instantiation failed, it still satisfies the genericType interface because + // it has type parameters, too. + ityp := check.instance(x.Pos(), gtyp, targs, nil, check.context()) + inst, _ := ityp.(genericType) + if inst == nil { + return Typ[Invalid] + } // For Named types, orig.tparams may not be set up, so we need to do expansion later. check.later(func() { diff --git a/src/cmd/compile/internal/types2/universe.go b/src/cmd/compile/internal/types2/universe.go index 9c76ac23730f0f..7664a53579ab3b 100644 --- a/src/cmd/compile/internal/types2/universe.go +++ b/src/cmd/compile/internal/types2/universe.go @@ -21,6 +21,7 @@ var Unsafe *Package var ( universeIota Object + universeBool Type universeByte Type // uint8 alias, but has name "byte" universeRune Type // int32 alias, but has name "rune" universeAnyNoAlias *TypeName @@ -275,6 +276,7 @@ func init() { defPredeclaredFuncs() universeIota = Universe.Lookup("iota") + universeBool = Universe.Lookup("bool").Type() universeByte = Universe.Lookup("byte").Type() universeRune = Universe.Lookup("rune").Type() universeError = Universe.Lookup("error").Type() diff --git a/src/cmd/compile/internal/walk/compare.go b/src/cmd/compile/internal/walk/compare.go index 25160008ee90ba..d3a91f30b9e8d4 100644 --- a/src/cmd/compile/internal/walk/compare.go +++ b/src/cmd/compile/internal/walk/compare.go @@ -317,8 +317,17 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { } func walkCompareInterface(n *ir.BinaryExpr, init *ir.Nodes) ir.Node { + swap := n.X.Op() != ir.OCONVIFACE && n.Y.Op() == ir.OCONVIFACE n.Y = cheapExpr(n.Y, init) n.X = cheapExpr(n.X, init) + if swap { + // Put the concrete type first in the comparison. + // This passes a constant type (itab) to efaceeq (ifaceeq) + // which is easier to match against in rewrite rules. + // See issue 70738. + n.X, n.Y = n.Y, n.X + } + eqtab, eqdata := compare.EqInterface(n.X, n.Y) var cmp ir.Node if n.Op() == ir.OEQ { diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go index 858fc706ab9783..8967b7dbba4840 100644 --- a/src/cmd/compile/internal/walk/order.go +++ b/src/cmd/compile/internal/walk/order.go @@ -334,6 +334,14 @@ func (o *orderState) mapKeyTemp(outerPos src.XPos, t *types.Type, n ir.Node) ir. // It would be nice to handle these generally, but because // []byte keys are not allowed in maps, the use of string(k) // comes up in important cases in practice. See issue 3512. +// +// Note that this code does not handle the case: +// +// s := string(k) +// x = m[s] +// +// Cases like this are handled during SSA, search for slicebytetostring +// in ../ssa/_gen/generic.rules. func mapKeyReplaceStrConv(n ir.Node) bool { var replaced bool switch n.Op() { diff --git a/src/cmd/compile/internal/walk/range.go b/src/cmd/compile/internal/walk/range.go index 27e71425c1b015..ede9f2182d494f 100644 --- a/src/cmd/compile/internal/walk/range.go +++ b/src/cmd/compile/internal/walk/range.go @@ -244,19 +244,24 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // depends on layout of iterator struct. // See cmd/compile/internal/reflectdata/reflect.go:MapIterType var keysym, elemsym *types.Sym + var iterInit, iterNext string if buildcfg.Experiment.SwissMap { keysym = th.Field(0).Sym elemsym = th.Field(1).Sym // ditto + iterInit = "mapIterStart" + iterNext = "mapIterNext" } else { keysym = th.Field(0).Sym elemsym = th.Field(1).Sym // ditto + iterInit = "mapiterinit" + iterNext = "mapiternext" } - fn := typecheck.LookupRuntime("mapiterinit", t.Key(), t.Elem(), th) + fn := typecheck.LookupRuntime(iterInit, t.Key(), t.Elem(), th) init = append(init, mkcallstmt1(fn, reflectdata.RangeMapRType(base.Pos, nrange), ha, typecheck.NodAddr(hit))) nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), typecheck.NodNil()) - fn = typecheck.LookupRuntime("mapiternext", th) + fn = typecheck.LookupRuntime(iterNext, th) nfor.Post = mkcallstmt1(fn, typecheck.NodAddr(hit)) key := ir.NewStarExpr(base.Pos, typecheck.ConvNop(ir.NewSelectorExpr(base.Pos, ir.ODOT, hit, keysym), types.NewPtr(t.Key()))) @@ -337,7 +342,10 @@ func walkRange(nrange *ir.RangeStmt) ir.Node { // if hv2 < utf8.RuneSelf nif := ir.NewIfStmt(base.Pos, nil, nil, nil) - nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLT, hv2, ir.NewInt(base.Pos, utf8.RuneSelf)) + + // On x86, hv2 <= 127 is shorter to encode than hv2 < 128 + // Doesn't hurt other archs. + nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OLE, hv2, ir.NewInt(base.Pos, utf8.RuneSelf-1)) // hv1++ nif.Body = []ir.Node{ir.NewAssignStmt(base.Pos, hv1, ir.NewBinaryExpr(base.Pos, ir.OADD, hv1, ir.NewInt(base.Pos, 1)))} diff --git a/src/cmd/covdata/dump.go b/src/cmd/covdata/dump.go index cbbeae0a8020de..e141689b00e563 100644 --- a/src/cmd/covdata/dump.go +++ b/src/cmd/covdata/dump.go @@ -331,7 +331,7 @@ func (d *dstate) Finish() { d.format.EmitFuncs(os.Stdout) } if d.textfmtoutf != nil { - if err := d.format.EmitTextual(d.textfmtoutf); err != nil { + if err := d.format.EmitTextual(nil, d.textfmtoutf); err != nil { fatal("writing to %s: %v", *textfmtoutflag, err) } } diff --git a/src/cmd/covdata/tool_test.go b/src/cmd/covdata/tool_test.go index 757a245047eddf..730adf4c8e62e0 100644 --- a/src/cmd/covdata/tool_test.go +++ b/src/cmd/covdata/tool_test.go @@ -9,7 +9,6 @@ import ( "flag" "fmt" "internal/coverage/pods" - "internal/goexperiment" "internal/testenv" "log" "os" @@ -150,9 +149,6 @@ const debugWorkDir = false func TestCovTool(t *testing.T) { testenv.MustHaveGoBuild(t) - if !goexperiment.CoverageRedesign { - t.Skipf("stubbed out due to goexperiment.CoverageRedesign=false") - } dir := tempDir(t) if testing.Short() { t.Skip() diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 1f467647f56143..4fcc508f8ed48e 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -16,6 +16,7 @@ import ( "os/exec" "path/filepath" "regexp" + "slices" "sort" "strconv" "strings" @@ -104,16 +105,6 @@ var okgoos = []string{ "aix", } -// find reports the first index of p in l[0:n], or else -1. -func find(p string, l []string) int { - for i, s := range l { - if p == s { - return i - } - } - return -1 -} - // xinit handles initialization of the various global state, like goroot and goarch. func xinit() { b := os.Getenv("GOROOT") @@ -134,7 +125,7 @@ func xinit() { b = gohostos } goos = b - if find(goos, okgoos) < 0 { + if slices.Index(okgoos, goos) < 0 { fatalf("unknown $GOOS %s", goos) } @@ -202,7 +193,7 @@ func xinit() { if b != "" { gohostarch = b } - if find(gohostarch, okgoarch) < 0 { + if slices.Index(okgoarch, gohostarch) < 0 { fatalf("unknown $GOHOSTARCH %s", gohostarch) } @@ -211,7 +202,7 @@ func xinit() { b = gohostarch } goarch = b - if find(goarch, okgoarch) < 0 { + if slices.Index(okgoarch, goarch) < 0 { fatalf("unknown $GOARCH %s", goarch) } diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 5a981f8bc147de..b137c7db7990bd 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -18,6 +18,7 @@ import ( "reflect" "regexp" "runtime" + "slices" "strconv" "strings" "time" @@ -280,12 +281,7 @@ func (t *tester) shouldRunTest(name string) bool { if len(t.runNames) == 0 { return true } - for _, runName := range t.runNames { - if runName == name { - return true - } - } - return false + return slices.Contains(t.runNames, name) } func (t *tester) maybeLogMetadata() error { @@ -716,9 +712,9 @@ func (t *tester) registerTests() { // Check that all crypto packages compile (and test correctly, in longmode) with fips. if t.fipsSupported() { // Test standard crypto packages with fips140=on. - t.registerTest("GODEBUG=fips140=on go test crypto/...", &goTest{ + t.registerTest("GOFIPS140=latest go test crypto/...", &goTest{ variant: "gofips140", - env: []string{"GODEBUG=fips140=on"}, + env: []string{"GOFIPS140=latest"}, pkg: "crypto/...", }) @@ -752,8 +748,8 @@ func (t *tester) registerTests() { }) } - // GODEBUG=gcstoptheworld=2 tests. We only run these in long-test - // mode (with GO_TEST_SHORT=0) because this is just testing a + // GC debug mode tests. We only run these in long-test mode + // (with GO_TEST_SHORT=0) because this is just testing a // non-critical debug setting. if !t.compileOnly && !t.short { t.registerTest("GODEBUG=gcstoptheworld=2 archive/zip", @@ -764,6 +760,14 @@ func (t *tester) registerTests() { env: []string{"GODEBUG=gcstoptheworld=2"}, pkg: "archive/zip", }) + t.registerTest("GODEBUG=gccheckmark=1 runtime", + &goTest{ + variant: "runtime:gccheckmark", + timeout: 300 * time.Second, + short: true, + env: []string{"GODEBUG=gccheckmark=1"}, + pkg: "runtime", + }) } // morestack tests. We only run these in long-test mode @@ -876,16 +880,18 @@ func (t *tester) registerTests() { } if t.extLink() && !t.compileOnly { - t.registerTest("external linking, -buildmode=exe", - &goTest{ - variant: "exe_external", - timeout: 60 * time.Second, - buildmode: "exe", - ldflags: "-linkmode=external", - env: []string{"CGO_ENABLED=1"}, - pkg: "crypto/internal/fips140test", - runTests: "TestFIPSCheck", - }) + if goos != "android" { // Android does not support non-PIE linking + t.registerTest("external linking, -buildmode=exe", + &goTest{ + variant: "exe_external", + timeout: 60 * time.Second, + buildmode: "exe", + ldflags: "-linkmode=external", + env: []string{"CGO_ENABLED=1"}, + pkg: "crypto/internal/fips140test", + runTests: "TestFIPSCheck", + }) + } if t.externalLinkPIE() && !disablePIE { t.registerTest("external linking, -buildmode=pie", &goTest{ @@ -1795,6 +1801,8 @@ func isEnvSet(evar string) bool { } func (t *tester) fipsSupported() bool { + // Keep this in sync with [crypto/internal/fips140.Supported]. + // Use GOFIPS140 or GOEXPERIMENT=boringcrypto, but not both. if strings.Contains(goexperiment, "boringcrypto") { return false @@ -1808,6 +1816,7 @@ func (t *tester) fipsSupported() bool { case goarch == "wasm", goos == "windows" && goarch == "386", goos == "windows" && goarch == "arm", + goos == "openbsd", goos == "aix": return false } diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go index 502de097f50688..a199991c21d42e 100644 --- a/src/cmd/doc/main.go +++ b/src/cmd/doc/main.go @@ -44,17 +44,26 @@ package main import ( "bytes" + "context" + "errors" "flag" "fmt" "go/build" "go/token" "io" "log" + "net" + "net/http" "os" + "os/exec" + "os/signal" "path" "path/filepath" "strings" + "time" + "cmd/internal/browser" + "cmd/internal/quoted" "cmd/internal/telemetry/counter" ) @@ -66,6 +75,7 @@ var ( showCmd bool // -cmd flag showSrc bool // -src flag short bool // -short flag + serveHTTP bool // -http flag ) // usage is a replacement usage function for the flags package. @@ -107,6 +117,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) { flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command") flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol") flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol") + flagSet.BoolVar(&serveHTTP, "http", false, "serve HTML docs over HTTP") flagSet.Parse(args) counter.Inc("doc/invocations") counter.CountFlags("doc/flag:", *flag.CommandLine) @@ -152,6 +163,9 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) { panic(e) }() + if serveHTTP { + return doPkgsite(pkg, symbol, method) + } switch { case symbol == "": pkg.packageDoc() // The package exists, so we got some output. @@ -168,6 +182,91 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) { } } +func doPkgsite(pkg *Package, symbol, method string) error { + ctx := context.Background() + + cmdline := "go run golang.org/x/pkgsite/cmd/pkgsite@latest -gorepo=" + buildCtx.GOROOT + words, err := quoted.Split(cmdline) + port, err := pickUnusedPort() + if err != nil { + return fmt.Errorf("failed to find port for documentation server: %v", err) + } + addr := fmt.Sprintf("localhost:%d", port) + words = append(words, fmt.Sprintf("-http=%s", addr)) + cmd := exec.CommandContext(context.Background(), words[0], words[1:]...) + cmd.Stdout = os.Stderr + cmd.Stderr = os.Stderr + // Turn off the default signal handler for SIGINT (and SIGQUIT on Unix) + // and instead wait for the child process to handle the signal and + // exit before exiting ourselves. + signal.Ignore(signalsToIgnore...) + + if err := cmd.Start(); err != nil { + return fmt.Errorf("starting pkgsite: %v", err) + } + + // Wait for pkgsite to became available. + if !waitAvailable(ctx, addr) { + cmd.Cancel() + cmd.Wait() + return errors.New("could not connect to local documentation server") + } + + // Open web browser. + path := path.Join("http://"+addr, pkg.build.ImportPath) + object := symbol + if symbol != "" && method != "" { + object = symbol + "." + method + } + if object != "" { + path = path + "#" + object + } + if ok := browser.Open(path); !ok { + cmd.Cancel() + cmd.Wait() + return errors.New("failed to open browser") + } + + // Wait for child to terminate. We expect the child process to receive signals from + // this terminal and terminate in a timely manner, so this process will terminate + // soon after. + return cmd.Wait() +} + +// pickUnusedPort finds an unused port by trying to listen on port 0 +// and letting the OS pick a port, then closing that connection and +// returning that port number. +// This is inherently racy. +func pickUnusedPort() (int, error) { + l, err := net.Listen("tcp", "localhost:0") + if err != nil { + return 0, err + } + port := l.Addr().(*net.TCPAddr).Port + if err := l.Close(); err != nil { + return 0, err + } + return port, nil +} + +func waitAvailable(ctx context.Context, addr string) bool { + ctx, cancel := context.WithTimeout(ctx, 15*time.Second) + defer cancel() + for ctx.Err() == nil { + req, err := http.NewRequestWithContext(ctx, "HEAD", "http://"+addr, nil) + if err != nil { + log.Println(err) + return false + } + resp, err := http.DefaultClient.Do(req) + if err == nil { + resp.Body.Close() + return true + } + } + return false +} + // failMessage creates a nicely formatted error message when there is no result to show. func failMessage(paths []string, symbol, method string) error { var b bytes.Buffer diff --git a/src/cmd/doc/signal_notunix.go b/src/cmd/doc/signal_notunix.go new file mode 100644 index 00000000000000..3b8fa9e080354d --- /dev/null +++ b/src/cmd/doc/signal_notunix.go @@ -0,0 +1,13 @@ +// Copyright 2012 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. + +//go:build plan9 || windows + +package main + +import ( + "os" +) + +var signalsToIgnore = []os.Signal{os.Interrupt} diff --git a/src/cmd/doc/signal_unix.go b/src/cmd/doc/signal_unix.go new file mode 100644 index 00000000000000..52431c221b5ba1 --- /dev/null +++ b/src/cmd/doc/signal_unix.go @@ -0,0 +1,14 @@ +// Copyright 2012 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. + +//go:build unix || js || wasip1 + +package main + +import ( + "os" + "syscall" +) + +var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT} diff --git a/src/cmd/fix/cftype.go b/src/cmd/fix/cftype.go index d4fcc4485e5791..04ece9fe5b4079 100644 --- a/src/cmd/fix/cftype.go +++ b/src/cmd/fix/cftype.go @@ -67,14 +67,14 @@ func typefix(f *ast.File, badType func(string) bool) bool { return } v := reflect.ValueOf(n) - if v.Type().Kind() != reflect.Pointer { + if v.Kind() != reflect.Pointer { return } if v.IsNil() { return } v = v.Elem() - if v.Type().Kind() != reflect.Struct { + if v.Kind() != reflect.Struct { return } for i := 0; i < v.NumField(); i++ { diff --git a/src/cmd/go.mod b/src/cmd/go.mod index b29321de7b4244..99c4046490c7d0 100644 --- a/src/cmd/go.mod +++ b/src/cmd/go.mod @@ -1,21 +1,21 @@ module cmd -go 1.24 +go 1.25 require ( - github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 - golang.org/x/arch v0.12.0 - golang.org/x/build v0.0.0-20241205234318-b850320af2a4 - golang.org/x/mod v0.22.0 - golang.org/x/sync v0.10.0 - golang.org/x/sys v0.28.0 - golang.org/x/telemetry v0.0.0-20241204182053-c0ac0e154df3 - golang.org/x/term v0.27.0 - golang.org/x/tools v0.28.0 + github.com/google/pprof v0.0.0-20250208200701-d0013a598941 + golang.org/x/arch v0.14.0 + golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63 + golang.org/x/mod v0.23.0 + golang.org/x/sync v0.11.0 + golang.org/x/sys v0.30.0 + golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a + golang.org/x/term v0.29.0 + golang.org/x/tools v0.30.1-0.20250212161021-f9aad7054b5f ) require ( github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/text v0.22.0 // indirect rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef // indirect ) diff --git a/src/cmd/go.sum b/src/cmd/go.sum index 5c262454d5b653..78f797c648efe4 100644 --- a/src/cmd/go.sum +++ b/src/cmd/go.sum @@ -1,28 +1,28 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs= -github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc= +github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd h1:EVX1s+XNss9jkRW9K6XGJn2jL2lB1h5H804oKPsxOec= github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg= -golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/build v0.0.0-20241205234318-b850320af2a4 h1:ri5CIHQTJCd3jd0Jez97HiPE+VMT0hFNKqLHn2EjrXk= -golang.org/x/build v0.0.0-20241205234318-b850320af2a4/go.mod h1:9O1P9bdbWH7KXtcbo+6amI/59H5mNq7+CTE1eKqNsjg= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/telemetry v0.0.0-20241204182053-c0ac0e154df3 h1:rCLsPBq7l0E9Z451UgkWFkaWYhgt7dGmAlpD6hLjK5I= -golang.org/x/telemetry v0.0.0-20241204182053-c0ac0e154df3/go.mod h1:8h4Hgq+jcTvCDv2+i7NrfWwpYHcESleo2nGHxLbFLJ4= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4= +golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63 h1:QZ8/V1B4oK7N5t6w0zX5dAxFIHt0WaTX+r1z29cWXjY= +golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63/go.mod h1:JhINjMoWj8G2oLkaBLNDBIr/GLqJNOkCr4XzFWWYCf4= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a h1:3fgycqG+90xOafOruMBVZXa8DUeOt5qbGLjQoNvZ8Ew= +golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a/go.mod h1:Ng+6E7PnWNge4EifZkPKeQUnm5iyAoH8qQgw3pLCiF4= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/tools v0.30.1-0.20250212161021-f9aad7054b5f h1:wN7/h1uT0B8rVpI6iWEPBC6qO1tdoMaNR6cOwdqqy/s= +golang.org/x/tools v0.30.1-0.20250212161021-f9aad7054b5f/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8= rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ= diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go index 3a4473902cb8f1..6f0cb1b6981020 100644 --- a/src/cmd/go/alldocs.go +++ b/src/cmd/go/alldocs.go @@ -739,11 +739,6 @@ // // For more about specifying packages, see 'go help packages'. // -// This text describes the behavior of get using modules to manage source -// code and dependencies. If instead the go command is running in GOPATH -// mode, the details of get's flags and effects change, as does 'go help get'. -// See 'go help gopath-get'. -// // See also: go build, go install, go clean, go mod. // // # Compile and install packages and dependencies @@ -1895,7 +1890,7 @@ // The rule for a match in the cache is that the run involves the same // test binary and the flags on the command line come entirely from a // restricted set of 'cacheable' test flags, defined as -benchtime, -cpu, -// -list, -parallel, -run, -short, -timeout, -failfast, -fullpath and -v. +// -list, -parallel, -run, -short, -skip, -timeout, -failfast, -fullpath and -v. // If a run of go test has any test or non-test flags outside this set, // the result is not cached. To disable test caching, use any test flag // or argument other than the cacheable flags. The idiomatic way to disable @@ -1958,6 +1953,13 @@ // The -n flag causes tool to print the command that would be // executed but not execute it. // +// The -modfile=file.mod build flag causes tool to use an alternate file +// instead of the go.mod in the module root directory. +// +// Tool also provides the -C, -overlay, and -modcacherw build flags. +// +// For more about build flags, see 'go help build'. +// // For more about each builtin tool command, see 'go doc cmd/'. // // # Print Go version @@ -2105,8 +2107,8 @@ // (or ppc64le.power8, ppc64le.power9, and ppc64le.power10) // feature build tags. // - For GOARCH=riscv64, -// GORISCV64=rva20u64 and rva22u64 correspond to the riscv64.rva20u64 -// and riscv64.rva22u64 build tags. +// GORISCV64=rva20u64, rva22u64 and rva23u64 correspond to the riscv64.rva20u64, +// riscv64.rva22u64 and riscv64.rva23u64 build tags. // - For GOARCH=wasm, GOWASM=satconv and signext // correspond to the wasm.satconv and wasm.signext feature build tags. // @@ -2186,7 +2188,7 @@ // fields of all events to reconstruct the text format output, as it would // have appeared from go build without the -json flag. // -// Note that there may also be non-JSON error text on stdnard error, even +// Note that there may also be non-JSON error text on standard error, even // with the -json flag. Typically, this indicates an early, serious error. // Consumers should be robust to this. // @@ -2250,7 +2252,7 @@ // // The second is the SWIG program, which is a general tool for // interfacing between languages. For information on SWIG see -// http://swig.org/. When running go build, any file with a .swig +// https://swig.org/. When running go build, any file with a .swig // extension will be passed to SWIG. Any file with a .swigcxx extension // will be passed to SWIG with the -c++ option. // @@ -2338,9 +2340,14 @@ // GOCACHE // The directory where the go command will store cached // information for reuse in future builds. +// GOCACHEPROG +// A command (with optional space-separated flags) that implements an +// external go command build cache. +// See 'go doc cmd/go/internal/cacheprog'. // GODEBUG -// Enable various debugging facilities. See https://go.dev/doc/godebug -// for details. +// Enable various debugging facilities for programs built with Go, +// including the go command. Cannot be set using 'go env -w'. +// See https://go.dev/doc/godebug for details. // GOENV // The location of the Go environment configuration file. // Cannot be set using 'go env -w'. @@ -2448,6 +2455,11 @@ // GOARM // For GOARCH=arm, the ARM architecture for which to compile. // Valid values are 5, 6, 7. +// When the Go tools are built on an arm system, +// the default value is set based on what the build system supports. +// When the Go tools are not built on an arm system +// (that is, when building a cross-compiler), +// the default value is 7. // The value can be followed by an option specifying how to implement floating point instructions. // Valid options are ,softfloat (default for 5) and ,hardfloat (default for 6 and 7). // GOARM64 @@ -2468,8 +2480,9 @@ // Valid values are power8 (default), power9, power10. // GORISCV64 // For GOARCH=riscv64, the RISC-V user-mode application profile for which -// to compile. Valid values are rva20u64 (default), rva22u64. +// to compile. Valid values are rva20u64 (default), rva22u64, rva23u64. // See https://github.com/riscv/riscv-profiles/blob/main/src/profiles.adoc +// and https://github.com/riscv/riscv-profiles/blob/main/src/rva23-profile.adoc // GOWASM // For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use. // Valid values are satconv, signext. @@ -2479,7 +2492,6 @@ // GOCOVERDIR // Directory into which to write code coverage data files // generated by running a "go build -cover" binary. -// Requires that GOEXPERIMENT=coverageredesign is enabled. // // Special-purpose environment variables: // @@ -2612,7 +2624,7 @@ // Example: Data // // If the server responds with any 4xx code, the go command will write the -// following to the programs' stdin: +// following to the program's stdin: // Response = StatusLine { HeaderLine } BlankLine . // StatusLine = Protocol Space Status '\n' . // Protocol = /* HTTP protocol */ . @@ -2627,8 +2639,7 @@ // Content-Type: text/plain; charset=utf-8 // Date: Thu, 07 Nov 2024 18:43:09 GMT // -// Note: at least for HTTP 1.1, the contents written to stdin can be parsed -// as an HTTP response. +// Note: it is safe to use net/http.ReadResponse to parse this input. // // Before the first HTTPS fetch, the go command will invoke each GOAUTH // command in the list with no additional arguments and no input. @@ -2913,12 +2924,12 @@ // import "example.org/user/foo.hg" // // denotes the root directory of the Mercurial repository at -// example.org/user/foo or foo.hg, and +// example.org/user/foo, and // // import "example.org/repo.git/foo/bar" // // denotes the foo/bar directory of the Git repository at -// example.org/repo or repo.git. +// example.org/repo. // // When a version control system supports multiple protocols, // each is tried in turn when downloading. For example, a Git @@ -2969,11 +2980,7 @@ // same meta tag and then git clone https://code.org/r/p/exproj into // GOPATH/src/example.org. // -// When using GOPATH, downloaded packages are written to the first directory -// listed in the GOPATH environment variable. -// (See 'go help gopath-get' and 'go help gopath'.) -// -// When using modules, downloaded packages are stored in the module cache. +// Downloaded packages are stored in the module cache. // See https://golang.org/ref/mod#module-cache. // // When using modules, an additional variant of the go-import meta tag is diff --git a/src/cmd/go/internal/auth/auth.go b/src/cmd/go/internal/auth/auth.go index b008e9c2818f2c..83c28d160c9d90 100644 --- a/src/cmd/go/internal/auth/auth.go +++ b/src/cmd/go/internal/auth/auth.go @@ -12,7 +12,6 @@ import ( "log" "net/http" "os" - "path" "path/filepath" "slices" "strings" @@ -71,9 +70,15 @@ func runGoAuth(client *http.Client, res *http.Response, url string) { case "netrc": lines, err := readNetrc() if err != nil { - base.Fatalf("go: could not parse netrc (GOAUTH=%s): %v", cfg.GOAUTH, err) + cmdErrs = append(cmdErrs, fmt.Errorf("GOAUTH=%s: %v", command, err)) + continue } - for _, l := range lines { + // Process lines in reverse so that if the same machine is listed + // multiple times, we end up saving the earlier one + // (overwriting later ones). This matches the way the go command + // worked before GOAUTH. + for i := len(lines) - 1; i >= 0; i-- { + l := lines[i] r := http.Request{Header: make(http.Header)} r.SetBasicAuth(l.login, l.password) storeCredential(l.machine, r.Header) @@ -123,7 +128,8 @@ func runGoAuth(client *http.Client, res *http.Response, url string) { // If no GOAUTH command provided a credential for the given url // and an error occurred, log the error. if cfg.BuildX && url != "" { - if ok := loadCredential(&http.Request{}, url); !ok && len(cmdErrs) > 0 { + req := &http.Request{Header: make(http.Header)} + if ok := loadCredential(req, url); !ok && len(cmdErrs) > 0 { log.Printf("GOAUTH encountered errors for %s:", url) for _, err := range cmdErrs { log.Printf(" %v", err) @@ -137,11 +143,13 @@ func runGoAuth(client *http.Client, res *http.Response, url string) { func loadCredential(req *http.Request, url string) bool { currentPrefix := strings.TrimPrefix(url, "https://") // Iteratively try prefixes, moving up the path hierarchy. - for currentPrefix != "/" && currentPrefix != "." && currentPrefix != "" { + for { headers, ok := credentialCache.Load(currentPrefix) if !ok { - // Move to the parent directory. - currentPrefix = path.Dir(currentPrefix) + currentPrefix, _, ok = strings.Cut(currentPrefix, "/") + if !ok { + return false + } continue } for key, values := range headers.(http.Header) { @@ -151,7 +159,6 @@ func loadCredential(req *http.Request, url string) bool { } return true } - return false } // storeCredential caches or removes credentials (represented by HTTP headers) diff --git a/src/cmd/go/internal/auth/auth_test.go b/src/cmd/go/internal/auth/auth_test.go index c7b4851e2882aa..c1bbf4b1a91e6f 100644 --- a/src/cmd/go/internal/auth/auth_test.go +++ b/src/cmd/go/internal/auth/auth_test.go @@ -25,7 +25,29 @@ func TestCredentialCache(t *testing.T) { got := &http.Request{Header: make(http.Header)} ok := loadCredential(got, tc.machine) if !ok || !reflect.DeepEqual(got.Header, want.Header) { - t.Errorf("loadCredential:\nhave %q\nwant %q", got.Header, want.Header) + t.Errorf("loadCredential(%q):\nhave %q\nwant %q", tc.machine, got.Header, want.Header) + } + } + + // Having stored those credentials, we should be able to look up longer URLs too. + extraCases := []netrcLine{ + {"https://api.github.com/foo", "user", "pwd"}, + {"https://api.github.com/foo/bar/baz", "user", "pwd"}, + {"https://example.com/abc", "", ""}, + {"https://example.com/?/../api.github.com/", "", ""}, + {"https://example.com/?/../api.github.com", "", ""}, + {"https://example.com/../api.github.com/", "", ""}, + {"https://example.com/../api.github.com", "", ""}, + } + for _, tc := range extraCases { + want := http.Request{Header: make(http.Header)} + if tc.login != "" { + want.SetBasicAuth(tc.login, tc.password) + } + got := &http.Request{Header: make(http.Header)} + loadCredential(got, tc.machine) + if !reflect.DeepEqual(got.Header, want.Header) { + t.Errorf("loadCredential(%q):\nhave %q\nwant %q", tc.machine, got.Header, want.Header) } } } diff --git a/src/cmd/go/internal/auth/gitauth.go b/src/cmd/go/internal/auth/gitauth.go index b28cb54453517c..29d2852814b450 100644 --- a/src/cmd/go/internal/auth/gitauth.go +++ b/src/cmd/go/internal/auth/gitauth.go @@ -6,6 +6,7 @@ // // See https://git-scm.com/docs/gitcredentials or run 'man gitcredentials' for // information on how to configure 'git credential'. + package auth import ( diff --git a/src/cmd/go/internal/auth/httputils.go b/src/cmd/go/internal/auth/httputils.go new file mode 100644 index 00000000000000..7f7bf03669ecad --- /dev/null +++ b/src/cmd/go/internal/auth/httputils.go @@ -0,0 +1,174 @@ +// 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. + +// Code copied from x/net/http/httpguts/httplex.go + +package auth + +var isTokenTable = [256]bool{ + '!': true, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '*': true, + '+': true, + '-': true, + '.': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'W': true, + 'V': true, + 'X': true, + 'Y': true, + 'Z': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '|': true, + '~': true, +} + +// isLWS reports whether b is linear white space, according +// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 +// +// LWS = [CRLF] 1*( SP | HT ) +func isLWS(b byte) bool { return b == ' ' || b == '\t' } + +// isCTL reports whether b is a control byte, according +// to http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 +// +// CTL = +func isCTL(b byte) bool { + const del = 0x7f // a CTL + return b < ' ' || b == del +} + +// validHeaderFieldName reports whether v is a valid HTTP/1.x header name. +// HTTP/2 imposes the additional restriction that uppercase ASCII +// letters are not allowed. +// +// RFC 7230 says: +// +// header-field = field-name ":" OWS field-value OWS +// field-name = token +// token = 1*tchar +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +func validHeaderFieldName(v string) bool { + if len(v) == 0 { + return false + } + for i := 0; i < len(v); i++ { + if !isTokenTable[v[i]] { + return false + } + } + return true +} + +// validHeaderFieldValue reports whether v is a valid "field-value" according to +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 : +// +// message-header = field-name ":" [ field-value ] +// field-value = *( field-content | LWS ) +// field-content = +// +// http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2.2 : +// +// TEXT = +// LWS = [CRLF] 1*( SP | HT ) +// CTL = +// +// RFC 7230 says: +// +// field-value = *( field-content / obs-fold ) +// obj-fold = N/A to http2, and deprecated +// field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] +// field-vchar = VCHAR / obs-text +// obs-text = %x80-FF +// VCHAR = "any visible [USASCII] character" +// +// http2 further says: "Similarly, HTTP/2 allows header field values +// that are not valid. While most of the values that can be encoded +// will not alter header field parsing, carriage return (CR, ASCII +// 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII +// 0x0) might be exploited by an attacker if they are translated +// verbatim. Any request or response that contains a character not +// permitted in a header field value MUST be treated as malformed +// (Section 8.1.2.6). Valid characters are defined by the +// field-content ABNF rule in Section 3.2 of [RFC7230]." +// +// This function does not (yet?) properly handle the rejection of +// strings that begin or end with SP or HTAB. +func validHeaderFieldValue(v string) bool { + for i := 0; i < len(v); i++ { + b := v[i] + if isCTL(b) && !isLWS(b) { + return false + } + } + return true +} diff --git a/src/cmd/go/internal/auth/userauth.go b/src/cmd/go/internal/auth/userauth.go index 0e54a83e318731..2649a9c271dce3 100644 --- a/src/cmd/go/internal/auth/userauth.go +++ b/src/cmd/go/internal/auth/userauth.go @@ -2,18 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package auth provides access to user-provided authentication credentials. package auth import ( - "bufio" - "bytes" "cmd/internal/quoted" "fmt" - "io" "maps" "net/http" - "net/textproto" + "net/url" "os/exec" "strings" ) @@ -42,7 +38,7 @@ func runAuthCommand(command string, url string, res *http.Response) (map[string] if err != nil { return nil, fmt.Errorf("could not run command %s: %v\n%s", command, err, cmd.Stderr) } - credentials, err := parseUserAuth(bytes.NewReader(out)) + credentials, err := parseUserAuth(string(out)) if err != nil { return nil, fmt.Errorf("cannot parse output of GOAUTH command %s: %v", command, err) } @@ -54,53 +50,47 @@ func runAuthCommand(command string, url string, res *http.Response) (map[string] // or an error if the data does not follow the expected format. // Returns an nil error and an empty map if the data is empty. // See the expected format in 'go help goauth'. -func parseUserAuth(data io.Reader) (map[string]http.Header, error) { +func parseUserAuth(data string) (map[string]http.Header, error) { credentials := make(map[string]http.Header) - reader := textproto.NewReader(bufio.NewReader(data)) - for { - // Return the processed credentials if the reader is at EOF. - if _, err := reader.R.Peek(1); err == io.EOF { - return credentials, nil + for data != "" { + var line string + var ok bool + var urls []string + // Parse URLS first. + for { + line, data, ok = strings.Cut(data, "\n") + if !ok { + return nil, fmt.Errorf("invalid format: missing empty line after URLs") + } + if line == "" { + break + } + u, err := url.ParseRequestURI(line) + if err != nil { + return nil, fmt.Errorf("could not parse URL %s: %v", line, err) + } + urls = append(urls, u.String()) } - urls, err := readURLs(reader) - if err != nil { - return nil, err - } - if len(urls) == 0 { - return nil, fmt.Errorf("invalid format: expected url prefix") - } - mimeHeader, err := reader.ReadMIMEHeader() - if err != nil { - return nil, err - } - header := http.Header(mimeHeader) - // Process the block (urls and headers). - credentialMap := mapHeadersToPrefixes(urls, header) - maps.Copy(credentials, credentialMap) - } -} - -// readURLs reads URL prefixes from the given reader until an empty line -// is encountered or an error occurs. It returns the list of URLs or an error -// if the format is invalid. -func readURLs(reader *textproto.Reader) (urls []string, err error) { - for { - line, err := reader.ReadLine() - if err != nil { - return nil, err - } - trimmedLine := strings.TrimSpace(line) - if trimmedLine != line { - return nil, fmt.Errorf("invalid format: leading or trailing white space") - } - if strings.HasPrefix(line, "https://") { - urls = append(urls, line) - } else if line == "" { - return urls, nil - } else { - return nil, fmt.Errorf("invalid format: expected url prefix or empty line") + // Parse Headers second. + header := make(http.Header) + for { + line, data, ok = strings.Cut(data, "\n") + if !ok { + return nil, fmt.Errorf("invalid format: missing empty line after headers") + } + if line == "" { + break + } + name, value, ok := strings.Cut(line, ": ") + value = strings.TrimSpace(value) + if !ok || !validHeaderFieldName(name) || !validHeaderFieldValue(value) { + return nil, fmt.Errorf("invalid format: invalid header line") + } + header.Add(name, value) } + maps.Copy(credentials, mapHeadersToPrefixes(urls, header)) } + return credentials, nil } // mapHeadersToPrefixes returns a mapping of prefix → http.Header without @@ -127,8 +117,8 @@ func buildCommand(command string) (*exec.Cmd, error) { func writeResponseToStdin(cmd *exec.Cmd, res *http.Response) error { var output strings.Builder output.WriteString(res.Proto + " " + res.Status + "\n") - if err := res.Header.Write(&output); err != nil { - return err + for k, v := range res.Header { + output.WriteString(k + ": " + strings.Join(v, ", ") + "\n") } output.WriteString("\n") cmd.Stdin = strings.NewReader(output.String()) diff --git a/src/cmd/go/internal/auth/userauth_test.go b/src/cmd/go/internal/auth/userauth_test.go index 91a5bb76ecd3bc..1b281ed3cdef45 100644 --- a/src/cmd/go/internal/auth/userauth_test.go +++ b/src/cmd/go/internal/auth/userauth_test.go @@ -7,7 +7,6 @@ package auth import ( "net/http" "reflect" - "strings" "testing" ) @@ -40,7 +39,7 @@ Data: Test567 "Test567", }, } - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s): %v", data, err) } @@ -100,10 +99,55 @@ Authorization: Basic GVuc2VzYW1lYWxhZGRpbjpvc Authorization: Basic 1lYWxhZGRplW1lYWxhZGRpbs Data: Test567 +`, + // Continuation in URL line + `https://example.com/ + Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l +`, + + // Continuation in header line + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb +`, + + // Continuation in multiple header lines + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb + Authorization: Basic dGhpc2lzYWxvbmdzdHJpbmc= +`, + + // Continuation with mixed spacing + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb +`, + + // Continuation with tab character + `https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l + Authorization: Basic jpvcGVuc2VzYW1lYWxhZGRpb +`, + // Continuation at the start of a block + ` https://example.com + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l +`, + + // Continuation after a blank line + `https://example.com + + +Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l `, } for _, tc := range testCases { - if credentials, err := parseUserAuth(strings.NewReader(tc)); err == nil { + if credentials, err := parseUserAuth(tc); err == nil { t.Errorf("parseUserAuth(%s) should have failed, but got: %v", tc, credentials) } } @@ -132,7 +176,7 @@ Data: Test567 "Test567", }, } - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s): %v", data, err) } @@ -146,7 +190,7 @@ func TestParseUserAuthEmptyHeader(t *testing.T) { data := "https://example.com\n\n\n" // Build the expected header header := http.Header{} - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s): %v", data, err) } @@ -159,7 +203,7 @@ func TestParseUserAuthEmptyHeader(t *testing.T) { func TestParseUserAuthEmpty(t *testing.T) { data := `` // Build the expected header - credentials, err := parseUserAuth(strings.NewReader(data)) + credentials, err := parseUserAuth(data) if err != nil { t.Errorf("parseUserAuth(%s) should have succeeded", data) } diff --git a/src/cmd/go/internal/base/limit.go b/src/cmd/go/internal/base/limit.go index b4160bde021c0d..4317432527a433 100644 --- a/src/cmd/go/internal/base/limit.go +++ b/src/cmd/go/internal/base/limit.go @@ -52,7 +52,7 @@ func AcquireNet() (release func(), err error) { } checker := new(netTokenChecker) - runtime.SetFinalizer(checker, (*netTokenChecker).panicUnreleased) + cleanup := runtime.AddCleanup(checker, func(_ int) { panic("internal error: net token acquired but not released") }, 0) return func() { if checker.released { @@ -62,7 +62,7 @@ func AcquireNet() (release func(), err error) { if hasToken { <-netLimitSem } - runtime.SetFinalizer(checker, nil) + cleanup.Stop() }, nil } @@ -78,7 +78,3 @@ type netTokenChecker struct { // “tiny allocator”. unusedAvoidTinyAllocator string } - -func (c *netTokenChecker) panicUnreleased() { - panic("internal error: net token acquired but not released") -} diff --git a/src/cmd/go/internal/cache/cache.go b/src/cmd/go/internal/cache/cache.go index 98bed2a595e1ba..1bef1db08c7a8f 100644 --- a/src/cmd/go/internal/cache/cache.go +++ b/src/cmd/go/internal/cache/cache.go @@ -38,8 +38,8 @@ type Cache interface { // Get returns the cache entry for the provided ActionID. // On miss, the error type should be of type *entryNotFoundError. // - // After a success call to Get, OutputFile(Entry.OutputID) must - // exist on disk for until Close is called (at the end of the process). + // After a successful call to Get, OutputFile(Entry.OutputID) must + // exist on disk until Close is called (at the end of the process). Get(ActionID) (Entry, error) // Put adds an item to the cache. @@ -50,14 +50,14 @@ type Cache interface { // As a special case, if the ReadSeeker is of type noVerifyReadSeeker, // the verification from GODEBUG=goverifycache=1 is skipped. // - // After a success call to Get, OutputFile(Entry.OutputID) must - // exist on disk for until Close is called (at the end of the process). + // After a successful call to Put, OutputFile(OutputID) must + // exist on disk until Close is called (at the end of the process). Put(ActionID, io.ReadSeeker) (_ OutputID, size int64, _ error) // Close is called at the end of the go process. Implementations can do // cache cleanup work at this phase, or wait for and report any errors from - // background cleanup work started earlier. Any cache trimming should in one - // process should not violate cause the invariants of this interface to be + // background cleanup work started earlier. Any cache trimming in one + // process should not cause the invariants of this interface to be // violated in another process. Namely, a cache trim from one process should // not delete an ObjectID from disk that was recently Get or Put from // another process. As a rule of thumb, don't trim things used in the last @@ -296,19 +296,19 @@ func GetBytes(c Cache, id ActionID) ([]byte, Entry, error) { // GetMmap looks up the action ID in the cache and returns // the corresponding output bytes. // GetMmap should only be used for data that can be expected to fit in memory. -func GetMmap(c Cache, id ActionID) ([]byte, Entry, error) { +func GetMmap(c Cache, id ActionID) ([]byte, Entry, bool, error) { entry, err := c.Get(id) if err != nil { - return nil, entry, err + return nil, entry, false, err } - md, err := mmap.Mmap(c.OutputFile(entry.OutputID)) + md, opened, err := mmap.Mmap(c.OutputFile(entry.OutputID)) if err != nil { - return nil, Entry{}, err + return nil, Entry{}, opened, err } if int64(len(md.Data)) != entry.Size { - return nil, Entry{}, &entryNotFoundError{Err: errors.New("file incomplete")} + return nil, Entry{}, true, &entryNotFoundError{Err: errors.New("file incomplete")} } - return md.Data, entry, nil + return md.Data, entry, true, nil } // OutputFile returns the name of the cache file storing output with the given OutputID. diff --git a/src/cmd/go/internal/cache/default.go b/src/cmd/go/internal/cache/default.go index 074f9115933739..f8e5696cbd1e84 100644 --- a/src/cmd/go/internal/cache/default.go +++ b/src/cmd/go/internal/cache/default.go @@ -54,8 +54,8 @@ func initDefaultCache() Cache { base.Fatalf("failed to initialize build cache at %s: %s\n", dir, err) } - if v := cfg.Getenv("GOCACHEPROG"); v != "" { - return startCacheProg(v, diskCache) + if cfg.GOCACHEPROG != "" { + return startCacheProg(cfg.GOCACHEPROG, diskCache) } return diskCache diff --git a/src/cmd/go/internal/cache/prog.go b/src/cmd/go/internal/cache/prog.go index e09620bac86c48..bfddf5e4deec83 100644 --- a/src/cmd/go/internal/cache/prog.go +++ b/src/cmd/go/internal/cache/prog.go @@ -7,6 +7,7 @@ package cache import ( "bufio" "cmd/go/internal/base" + "cmd/go/internal/cacheprog" "cmd/internal/quoted" "context" "crypto/sha256" @@ -38,7 +39,7 @@ type ProgCache struct { // can are the commands that the child process declared that it supports. // This is effectively the versioning mechanism. - can map[ProgCmd]bool + can map[cacheprog.Cmd]bool // fuzzDirCache is another Cache implementation to use for the FuzzDir // method. In practice this is the default GOCACHE disk-based @@ -55,7 +56,7 @@ type ProgCache struct { mu sync.Mutex // guards following fields nextID int64 - inFlight map[int64]chan<- *ProgResponse + inFlight map[int64]chan<- *cacheprog.Response outputFile map[OutputID]string // object => abs path on disk // writeMu serializes writing to the child process. @@ -63,95 +64,6 @@ type ProgCache struct { writeMu sync.Mutex } -// ProgCmd is a command that can be issued to a child process. -// -// If the interface needs to grow, we can add new commands or new versioned -// commands like "get2". -type ProgCmd string - -const ( - cmdGet = ProgCmd("get") - cmdPut = ProgCmd("put") - cmdClose = ProgCmd("close") -) - -// ProgRequest is the JSON-encoded message that's sent from cmd/go to -// the GOCACHEPROG child process over stdin. Each JSON object is on its -// own line. A ProgRequest of Type "put" with BodySize > 0 will be followed -// by a line containing a base64-encoded JSON string literal of the body. -type ProgRequest struct { - // ID is a unique number per process across all requests. - // It must be echoed in the ProgResponse from the child. - ID int64 - - // Command is the type of request. - // The cmd/go tool will only send commands that were declared - // as supported by the child. - Command ProgCmd - - // ActionID is non-nil for get and puts. - ActionID []byte `json:",omitempty"` // or nil if not used - - // OutputID is set for Type "put". - // - // Prior to Go 1.24, when GOCACHEPROG was still an experiment, this was - // accidentally named ObjectID. It was renamed to OutputID in Go 1.24. - OutputID []byte `json:",omitempty"` // or nil if not used - - // Body is the body for "put" requests. It's sent after the JSON object - // as a base64-encoded JSON string when BodySize is non-zero. - // It's sent as a separate JSON value instead of being a struct field - // send in this JSON object so large values can be streamed in both directions. - // The base64 string body of a ProgRequest will always be written - // immediately after the JSON object and a newline. - Body io.Reader `json:"-"` - - // BodySize is the number of bytes of Body. If zero, the body isn't written. - BodySize int64 `json:",omitempty"` - - // ObjectID is the accidental spelling of OutputID that was used prior to Go - // 1.24. - // - // Deprecated: use OutputID. This field is only populated temporarily for - // backwards compatibility with Go 1.23 and earlier when - // GOEXPERIMENT=gocacheprog is set. It will be removed in Go 1.25. - ObjectID []byte `json:",omitempty"` -} - -// ProgResponse is the JSON response from the child process to cmd/go. -// -// With the exception of the first protocol message that the child writes to its -// stdout with ID==0 and KnownCommands populated, these are only sent in -// response to a ProgRequest from cmd/go. -// -// ProgResponses can be sent in any order. The ID must match the request they're -// replying to. -type ProgResponse struct { - ID int64 // that corresponds to ProgRequest; they can be answered out of order - Err string `json:",omitempty"` // if non-empty, the error - - // KnownCommands is included in the first message that cache helper program - // writes to stdout on startup (with ID==0). It includes the - // ProgRequest.Command types that are supported by the program. - // - // This lets us extend the protocol gracefully over time (adding "get2", - // etc), or fail gracefully when needed. It also lets us verify the program - // wants to be a cache helper. - KnownCommands []ProgCmd `json:",omitempty"` - - // For Get requests. - - Miss bool `json:",omitempty"` // cache miss - OutputID []byte `json:",omitempty"` - Size int64 `json:",omitempty"` // in bytes - Time *time.Time `json:",omitempty"` // an Entry.Time; when the object was added to the docs - - // DiskPath is the absolute path on disk of the ObjectID corresponding - // a "get" request's ActionID (on cache hit) or a "put" request's - // provided ObjectID. - DiskPath string `json:",omitempty"` -} - // startCacheProg starts the prog binary (with optional space-separated flags) // and returns a Cache implementation that talks to it. // @@ -183,6 +95,8 @@ func startCacheProg(progAndArgs string, fuzzDirCache Cache) Cache { base.Fatalf("StdinPipe to GOCACHEPROG: %v", err) } cmd.Stderr = os.Stderr + // On close, we cancel the context. Rather than killing the helper, + // close its stdin. cmd.Cancel = in.Close if err := cmd.Start(); err != nil { @@ -197,14 +111,14 @@ func startCacheProg(progAndArgs string, fuzzDirCache Cache) Cache { stdout: out, stdin: in, bw: bufio.NewWriter(in), - inFlight: make(map[int64]chan<- *ProgResponse), + inFlight: make(map[int64]chan<- *cacheprog.Response), outputFile: make(map[OutputID]string), readLoopDone: make(chan struct{}), } // Register our interest in the initial protocol message from the child to // us, saying what it can do. - capResc := make(chan *ProgResponse, 1) + capResc := make(chan *cacheprog.Response, 1) pc.inFlight[0] = capResc pc.jenc = json.NewEncoder(pc.bw) @@ -219,7 +133,7 @@ func startCacheProg(progAndArgs string, fuzzDirCache Cache) Cache { case <-timer.C: log.Printf("# still waiting for GOCACHEPROG %v ...", prog) case capRes := <-capResc: - can := map[ProgCmd]bool{} + can := map[cacheprog.Cmd]bool{} for _, cmd := range capRes.KnownCommands { can[cmd] = true } @@ -236,9 +150,15 @@ func (c *ProgCache) readLoop(readLoopDone chan<- struct{}) { defer close(readLoopDone) jd := json.NewDecoder(c.stdout) for { - res := new(ProgResponse) + res := new(cacheprog.Response) if err := jd.Decode(res); err != nil { if c.closing.Load() { + c.mu.Lock() + for _, ch := range c.inFlight { + close(ch) + } + c.inFlight = nil + c.mu.Unlock() return // quietly } if err == io.EOF { @@ -261,13 +181,18 @@ func (c *ProgCache) readLoop(readLoopDone chan<- struct{}) { } } -func (c *ProgCache) send(ctx context.Context, req *ProgRequest) (*ProgResponse, error) { - resc := make(chan *ProgResponse, 1) +var errCacheprogClosed = errors.New("GOCACHEPROG program closed unexpectedly") + +func (c *ProgCache) send(ctx context.Context, req *cacheprog.Request) (*cacheprog.Response, error) { + resc := make(chan *cacheprog.Response, 1) if err := c.writeToChild(req, resc); err != nil { return nil, err } select { case res := <-resc: + if res == nil { + return nil, errCacheprogClosed + } if res.Err != "" { return nil, errors.New(res.Err) } @@ -277,8 +202,11 @@ func (c *ProgCache) send(ctx context.Context, req *ProgRequest) (*ProgResponse, } } -func (c *ProgCache) writeToChild(req *ProgRequest, resc chan<- *ProgResponse) (err error) { +func (c *ProgCache) writeToChild(req *cacheprog.Request, resc chan<- *cacheprog.Response) (err error) { c.mu.Lock() + if c.inFlight == nil { + return errCacheprogClosed + } c.nextID++ req.ID = c.nextID c.inFlight[req.ID] = resc @@ -287,7 +215,9 @@ func (c *ProgCache) writeToChild(req *ProgRequest, resc chan<- *ProgResponse) (e defer func() { if err != nil { c.mu.Lock() - delete(c.inFlight, req.ID) + if c.inFlight != nil { + delete(c.inFlight, req.ID) + } c.mu.Unlock() } }() @@ -328,7 +258,7 @@ func (c *ProgCache) writeToChild(req *ProgRequest, resc chan<- *ProgResponse) (e } func (c *ProgCache) Get(a ActionID) (Entry, error) { - if !c.can[cmdGet] { + if !c.can[cacheprog.CmdGet] { // They can't do a "get". Maybe they're a write-only cache. // // TODO(bradfitz,bcmills): figure out the proper error type here. Maybe @@ -338,8 +268,8 @@ func (c *ProgCache) Get(a ActionID) (Entry, error) { // error types on the Cache interface. return Entry{}, &entryNotFoundError{} } - res, err := c.send(c.ctx, &ProgRequest{ - Command: cmdGet, + res, err := c.send(c.ctx, &cacheprog.Request{ + Command: cacheprog.CmdGet, ActionID: a[:], }) if err != nil { @@ -395,7 +325,7 @@ func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64, return OutputID{}, 0, err } - if !c.can[cmdPut] { + if !c.can[cacheprog.CmdPut] { // Child is a read-only cache. Do nothing. return out, size, nil } @@ -407,8 +337,8 @@ func (c *ProgCache) Put(a ActionID, file io.ReadSeeker) (_ OutputID, size int64, deprecatedValue = out[:] } - res, err := c.send(c.ctx, &ProgRequest{ - Command: cmdPut, + res, err := c.send(c.ctx, &cacheprog.Request{ + Command: cacheprog.CmdPut, ActionID: a[:], OutputID: out[:], ObjectID: deprecatedValue, // TODO(bradfitz): remove in Go 1.25 @@ -432,10 +362,16 @@ func (c *ProgCache) Close() error { // First write a "close" message to the child so it can exit nicely // and clean up if it wants. Only after that exchange do we cancel // the context that kills the process. - if c.can[cmdClose] { - _, err = c.send(c.ctx, &ProgRequest{Command: cmdClose}) + if c.can[cacheprog.CmdClose] { + _, err = c.send(c.ctx, &cacheprog.Request{Command: cacheprog.CmdClose}) + if errors.Is(err, errCacheprogClosed) { + // Allow the child to quit without responding to close. + err = nil + } } + // Cancel the context, which will close the helper's stdin. c.ctxCancel() + // Wait until the helper closes its stdout. <-c.readLoopDone return err } diff --git a/src/cmd/go/internal/cacheprog/cacheprog.go b/src/cmd/go/internal/cacheprog/cacheprog.go new file mode 100644 index 00000000000000..a2796592df5fd3 --- /dev/null +++ b/src/cmd/go/internal/cacheprog/cacheprog.go @@ -0,0 +1,137 @@ +// Copyright 2024 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. + +// Package cacheprog defines the protocol for a GOCACHEPROG program. +// +// By default, the go command manages a build cache stored in the file system +// itself. GOCACHEPROG can be set to the name of a command (with optional +// space-separated flags) that implements the go command build cache externally. +// This permits defining a different cache policy. +// +// The go command will start the GOCACHEPROG as a subprocess and communicate +// with it via JSON messages over stdin/stdout. The subprocess's stderr will be +// connected to the go command's stderr. +// +// The subprocess should immediately send a [Response] with its capabilities. +// After that, the go command will send a stream of [Request] messages and the +// subprocess should reply to each [Request] with a [Response] message. +package cacheprog + +import ( + "io" + "time" +) + +// Cmd is a command that can be issued to a child process. +// +// If the interface needs to grow, the go command can add new commands or new +// versioned commands like "get2" in the future. The initial [Response] from +// the child process indicates which commands it supports. +type Cmd string + +const ( + // CmdPut tells the cache program to store an object in the cache. + // + // [Request.ActionID] is the cache key of this object. The cache should + // store [Request.OutputID] and [Request.Body] under this key for a + // later "get" request. It must also store the Body in a file in the local + // file system and return the path to that file in [Response.DiskPath], + // which must exist at least until a "close" request. + CmdPut = Cmd("put") + + // CmdGet tells the cache program to retrieve an object from the cache. + // + // [Request.ActionID] specifies the key of the object to get. If the + // cache does not contain this object, it should set [Response.Miss] to + // true. Otherwise, it should populate the fields of [Response], + // including setting [Response.OutputID] to the OutputID of the original + // "put" request and [Response.DiskPath] to the path of a local file + // containing the Body of the original "put" request. That file must + // continue to exist at least until a "close" request. + CmdGet = Cmd("get") + + // CmdClose requests that the cache program exit gracefully. + // + // The cache program should reply to this request and then exit + // (thus closing its stdout). + CmdClose = Cmd("close") +) + +// Request is the JSON-encoded message that's sent from the go command to +// the GOCACHEPROG child process over stdin. Each JSON object is on its own +// line. A ProgRequest of Type "put" with BodySize > 0 will be followed by a +// line containing a base64-encoded JSON string literal of the body. +type Request struct { + // ID is a unique number per process across all requests. + // It must be echoed in the Response from the child. + ID int64 + + // Command is the type of request. + // The go command will only send commands that were declared + // as supported by the child. + Command Cmd + + // ActionID is the cache key for "put" and "get" requests. + ActionID []byte `json:",omitempty"` // or nil if not used + + // OutputID is stored with the body for "put" requests. + // + // Prior to Go 1.24, when GOCACHEPROG was still an experiment, this was + // accidentally named ObjectID. It was renamed to OutputID in Go 1.24. + OutputID []byte `json:",omitempty"` // or nil if not used + + // Body is the body for "put" requests. It's sent after the JSON object + // as a base64-encoded JSON string when BodySize is non-zero. + // It's sent as a separate JSON value instead of being a struct field + // send in this JSON object so large values can be streamed in both directions. + // The base64 string body of a Request will always be written + // immediately after the JSON object and a newline. + Body io.Reader `json:"-"` + + // BodySize is the number of bytes of Body. If zero, the body isn't written. + BodySize int64 `json:",omitempty"` + + // ObjectID is the accidental spelling of OutputID that was used prior to Go + // 1.24. + // + // Deprecated: use OutputID. This field is only populated temporarily for + // backwards compatibility with Go 1.23 and earlier when + // GOEXPERIMENT=gocacheprog is set. It will be removed in Go 1.25. + ObjectID []byte `json:",omitempty"` +} + +// Response is the JSON response from the child process to the go command. +// +// With the exception of the first protocol message that the child writes to its +// stdout with ID==0 and KnownCommands populated, these are only sent in +// response to a Request from the go command. +// +// Responses can be sent in any order. The ID must match the request they're +// replying to. +type Response struct { + ID int64 // that corresponds to Request; they can be answered out of order + Err string `json:",omitempty"` // if non-empty, the error + + // KnownCommands is included in the first message that cache helper program + // writes to stdout on startup (with ID==0). It includes the + // Request.Command types that are supported by the program. + // + // This lets the go command extend the protocol gracefully over time (adding + // "get2", etc), or fail gracefully when needed. It also lets the go command + // verify the program wants to be a cache helper. + KnownCommands []Cmd `json:",omitempty"` + + // For "get" requests. + + Miss bool `json:",omitempty"` // cache miss + OutputID []byte `json:",omitempty"` // the ObjectID stored with the body + Size int64 `json:",omitempty"` // body size in bytes + Time *time.Time `json:",omitempty"` // when the object was put in the cache (optional; used for cache expiration) + + // For "get" and "put" requests. + + // DiskPath is the absolute path on disk of the body corresponding to a + // "get" (on cache hit) or "put" request's ActionID. + DiskPath string `json:",omitempty"` +} diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go index 6c2af99c2dfb09..3b9f27e91d517e 100644 --- a/src/cmd/go/internal/cfg/cfg.go +++ b/src/cmd/go/internal/cfg/cfg.go @@ -425,8 +425,9 @@ var ( GOROOTpkg string GOROOTsrc string - GOBIN = Getenv("GOBIN") - GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod")) + GOBIN = Getenv("GOBIN") + GOCACHEPROG, GOCACHEPROGChanged = EnvOrAndChanged("GOCACHEPROG", "") + GOMODCACHE, GOMODCACHEChanged = EnvOrAndChanged("GOMODCACHE", gopathDir("pkg/mod")) // Used in envcmd.MkEnv and build ID computations. GOARM64, goARM64Changed = EnvOrAndChanged("GOARM64", buildcfg.DefaultGOARM64) diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go index 19db68e4f8f01d..7c370d427f9c2f 100644 --- a/src/cmd/go/internal/envcmd/env.go +++ b/src/cmd/go/internal/envcmd/env.go @@ -85,6 +85,7 @@ func MkEnv() []cfg.EnvVar { {Name: "GOAUTH", Value: cfg.GOAUTH, Changed: cfg.GOAUTHChanged}, {Name: "GOBIN", Value: cfg.GOBIN}, {Name: "GOCACHE"}, + {Name: "GOCACHEPROG", Value: cfg.GOCACHEPROG, Changed: cfg.GOCACHEPROGChanged}, {Name: "GODEBUG", Value: os.Getenv("GODEBUG")}, {Name: "GOENV", Value: envFile, Changed: envFileChanged}, {Name: "GOEXE", Value: cfg.ExeSuffix}, diff --git a/src/cmd/go/internal/fips140/fips140.go b/src/cmd/go/internal/fips140/fips140.go index 7c04a94dd1fea3..328e06088e3d47 100644 --- a/src/cmd/go/internal/fips140/fips140.go +++ b/src/cmd/go/internal/fips140/fips140.go @@ -40,14 +40,8 @@ // // GOFIPS140=latest go build -work my/binary // -// will leave fips.o behind in $WORK/b001. Auditors like to be able to -// see that file. Accordingly, when [Enabled] returns true, -// [cmd/go/internal/work.Builder.useCache] arranges never to cache linker -// output, so that the link step always runs, and fips.o is always left -// behind in the link step. If this proves too slow, we could always -// cache fips.o as an extra link output and then restore it when -work is -// set, but we went a very long time never caching link steps at all, so -// not caching them in FIPS mode seems perfectly fine. +// will leave fips.o behind in $WORK/b001 +// (unless the build result is cached, of course). // // When GOFIPS140 is set to something besides off and latest, [Snapshot] // returns true, indicating that the build should replace the latest copy @@ -119,6 +113,10 @@ func Init() { if Snapshot() { fsys.Bind(Dir(), filepath.Join(cfg.GOROOT, "src/crypto/internal/fips140")) } + + if cfg.Experiment.BoringCrypto && Enabled() { + base.Fatalf("go: cannot use GOFIPS140 with GOEXPERIMENT=boringcrypto") + } } var initDone bool diff --git a/src/cmd/go/internal/help/helpdoc.go b/src/cmd/go/internal/help/helpdoc.go index e1240de710b7a9..4adabfbbd4b491 100644 --- a/src/cmd/go/internal/help/helpdoc.go +++ b/src/cmd/go/internal/help/helpdoc.go @@ -17,7 +17,7 @@ information on how to use it see the cgo documentation (go doc cmd/cgo). The second is the SWIG program, which is a general tool for interfacing between languages. For information on SWIG see -http://swig.org/. When running go build, any file with a .swig +https://swig.org/. When running go build, any file with a .swig extension will be passed to SWIG. Any file with a .swigcxx extension will be passed to SWIG with the -c++ option. @@ -214,12 +214,12 @@ For example, import "example.org/user/foo.hg" denotes the root directory of the Mercurial repository at -example.org/user/foo or foo.hg, and +example.org/user/foo, and import "example.org/repo.git/foo/bar" denotes the foo/bar directory of the Git repository at -example.org/repo or repo.git. +example.org/repo. When a version control system supports multiple protocols, each is tried in turn when downloading. For example, a Git @@ -270,11 +270,7 @@ the go tool will verify that https://example.org/?go-get=1 contains the same meta tag and then git clone https://code.org/r/p/exproj into GOPATH/src/example.org. -When using GOPATH, downloaded packages are written to the first directory -listed in the GOPATH environment variable. -(See 'go help gopath-get' and 'go help gopath'.) - -When using modules, downloaded packages are stored in the module cache. +Downloaded packages are stored in the module cache. See https://golang.org/ref/mod#module-cache. When using modules, an additional variant of the go-import meta tag is @@ -510,9 +506,14 @@ General-purpose environment variables: GOCACHE The directory where the go command will store cached information for reuse in future builds. + GOCACHEPROG + A command (with optional space-separated flags) that implements an + external go command build cache. + See 'go doc cmd/go/internal/cacheprog'. GODEBUG - Enable various debugging facilities. See https://go.dev/doc/godebug - for details. + Enable various debugging facilities for programs built with Go, + including the go command. Cannot be set using 'go env -w'. + See https://go.dev/doc/godebug for details. GOENV The location of the Go environment configuration file. Cannot be set using 'go env -w'. @@ -620,6 +621,11 @@ Architecture-specific environment variables: GOARM For GOARCH=arm, the ARM architecture for which to compile. Valid values are 5, 6, 7. + When the Go tools are built on an arm system, + the default value is set based on what the build system supports. + When the Go tools are not built on an arm system + (that is, when building a cross-compiler), + the default value is 7. The value can be followed by an option specifying how to implement floating point instructions. Valid options are ,softfloat (default for 5) and ,hardfloat (default for 6 and 7). GOARM64 @@ -640,8 +646,9 @@ Architecture-specific environment variables: Valid values are power8 (default), power9, power10. GORISCV64 For GOARCH=riscv64, the RISC-V user-mode application profile for which - to compile. Valid values are rva20u64 (default), rva22u64. + to compile. Valid values are rva20u64 (default), rva22u64, rva23u64. See https://github.com/riscv/riscv-profiles/blob/main/src/profiles.adoc + and https://github.com/riscv/riscv-profiles/blob/main/src/rva23-profile.adoc GOWASM For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use. Valid values are satconv, signext. @@ -651,7 +658,6 @@ Environment variables for use with code coverage: GOCOVERDIR Directory into which to write code coverage data files generated by running a "go build -cover" binary. - Requires that GOEXPERIMENT=coverageredesign is enabled. Special-purpose environment variables: @@ -946,8 +952,8 @@ The defined architecture feature build tags are: (or ppc64le.power8, ppc64le.power9, and ppc64le.power10) feature build tags. - For GOARCH=riscv64, - GORISCV64=rva20u64 and rva22u64 correspond to the riscv64.rva20u64 - and riscv64.rva22u64 build tags. + GORISCV64=rva20u64, rva22u64 and rva23u64 correspond to the riscv64.rva20u64, + riscv64.rva22u64 and riscv64.rva23u64 build tags. - For GOARCH=wasm, GOWASM=satconv and signext correspond to the wasm.satconv and wasm.signext feature build tags. @@ -1029,7 +1035,7 @@ command Example: Data If the server responds with any 4xx code, the go command will write the - following to the programs' stdin: + following to the program's stdin: Response = StatusLine { HeaderLine } BlankLine . StatusLine = Protocol Space Status '\n' . Protocol = /* HTTP protocol */ . @@ -1044,8 +1050,7 @@ command Content-Type: text/plain; charset=utf-8 Date: Thu, 07 Nov 2024 18:43:09 GMT - Note: at least for HTTP 1.1, the contents written to stdin can be parsed - as an HTTP response. + Note: it is safe to use net/http.ReadResponse to parse this input. Before the first HTTPS fetch, the go command will invoke each GOAUTH command in the list with no additional arguments and no input. @@ -1097,7 +1102,7 @@ Furthermore, as with TestEvent, parsers can simply concatenate the Output fields of all events to reconstruct the text format output, as it would have appeared from go build without the -json flag. -Note that there may also be non-JSON error text on stdnard error, even +Note that there may also be non-JSON error text on standard error, even with the -json flag. Typically, this indicates an early, serious error. Consumers should be robust to this. `, diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go index 04fdadef3fde69..d6cba5a4e0640f 100644 --- a/src/cmd/go/internal/list/list.go +++ b/src/cmd/go/internal/list/list.go @@ -347,9 +347,7 @@ func init() { CmdList.Run = runList // break init cycle // Omit build -json because list has its own -json work.AddBuildFlags(CmdList, work.OmitJSONFlag) - if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign { - work.AddCoverFlags(CmdList, nil) - } + work.AddCoverFlags(CmdList, nil) CmdList.Flag.Var(&listJsonFields, "json", "") } @@ -728,7 +726,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) { b.IsCmdList = true b.NeedExport = *listExport b.NeedCompiledGoFiles = *listCompiled - if cfg.Experiment.CoverageRedesign && cfg.BuildCover { + if cfg.BuildCover { load.PrepareForCoverageBuild(pkgs) } a := &work.Action{} diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index df790e1eaab616..0c4639ce82c321 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -8,7 +8,6 @@ package load import ( "bytes" "context" - "crypto/sha256" "encoding/json" "errors" "fmt" @@ -218,27 +217,26 @@ func (p *Package) IsTestOnly() bool { type PackageInternal struct { // Unexported fields are not part of the public API. Build *build.Package - Imports []*Package // this package's direct imports - CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports - RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports - ForceLibrary bool // this package is a library (even if named "main") - CmdlineFiles bool // package built from files listed on command line - CmdlinePkg bool // package listed on command line - CmdlinePkgLiteral bool // package listed as literal on command line (not via wildcard) - Local bool // imported via local path (./ or ../) - LocalPrefix string // interpret ./ and ../ imports relative to this prefix - ExeName string // desired name for temporary executable - FuzzInstrument bool // package should be instrumented for fuzzing - Cover CoverSetup // coverage mode and other setup info of -cover is being applied to this package - CoverVars map[string]*CoverVar // variables created by coverage analysis - OmitDebug bool // tell linker not to write debug information - GobinSubdir bool // install target would be subdir of GOBIN - BuildInfo *debug.BuildInfo // add this info to package main - TestmainGo *[]byte // content for _testmain.go - Embed map[string][]string // //go:embed comment mapping - OrigImportPath string // original import path before adding '_test' suffix - PGOProfile string // path to PGO profile - ForMain string // the main package if this package is built specifically for it + Imports []*Package // this package's direct imports + CompiledImports []string // additional Imports necessary when using CompiledGoFiles (all from standard library); 1:1 with the end of PackagePublic.Imports + RawImports []string // this package's original imports as they appear in the text of the program; 1:1 with the end of PackagePublic.Imports + ForceLibrary bool // this package is a library (even if named "main") + CmdlineFiles bool // package built from files listed on command line + CmdlinePkg bool // package listed on command line + CmdlinePkgLiteral bool // package listed as literal on command line (not via wildcard) + Local bool // imported via local path (./ or ../) + LocalPrefix string // interpret ./ and ../ imports relative to this prefix + ExeName string // desired name for temporary executable + FuzzInstrument bool // package should be instrumented for fuzzing + Cover CoverSetup // coverage mode and other setup info of -cover is being applied to this package + OmitDebug bool // tell linker not to write debug information + GobinSubdir bool // install target would be subdir of GOBIN + BuildInfo *debug.BuildInfo // add this info to package main + TestmainGo *[]byte // content for _testmain.go + Embed map[string][]string // //go:embed comment mapping + OrigImportPath string // original import path before adding '_test' suffix + PGOProfile string // path to PGO profile + ForMain string // the main package if this package is built specifically for it Asmflags []string // -asmflags for this package Gcflags []string // -gcflags for this package @@ -372,12 +370,6 @@ func (p *Package) Resolve(imports []string) []string { return all } -// CoverVar holds the name of the generated coverage variables targeting the named file. -type CoverVar struct { - File string // local file name - Var string // name of count struct -} - // CoverSetup holds parameters related to coverage setup for a given package (covermode, etc). type CoverSetup struct { Mode string // coverage mode for this package @@ -2627,7 +2619,7 @@ func LinkerDeps(p *Package) ([]string, error) { deps = append(deps, "runtime/asan") } // Building for coverage forces an import of runtime/coverage. - if cfg.BuildCover && cfg.Experiment.CoverageRedesign { + if cfg.BuildCover { deps = append(deps, "runtime/coverage") } @@ -3068,7 +3060,15 @@ func setPGOProfilePath(pkgs []*Package) { // CheckPackageErrors prints errors encountered loading pkgs and their // dependencies, then exits with a non-zero status if any errors were found. func CheckPackageErrors(pkgs []*Package) { - var anyIncomplete bool + PackageErrors(pkgs, func(p *Package) { + DefaultPrinter().Errorf(p, "%v", p.Error) + }) + base.ExitIfErrors() +} + +// PackageErrors calls report for errors encountered loading pkgs and their dependencies. +func PackageErrors(pkgs []*Package, report func(*Package)) { + var anyIncomplete, anyErrors bool for _, pkg := range pkgs { if pkg.Incomplete { anyIncomplete = true @@ -3078,11 +3078,14 @@ func CheckPackageErrors(pkgs []*Package) { all := PackageList(pkgs) for _, p := range all { if p.Error != nil { - DefaultPrinter().Errorf(p, "%v", p.Error) + report(p) + anyErrors = true } } } - base.ExitIfErrors() + if anyErrors { + return + } // Check for duplicate loads of the same package. // That should be impossible, but if it does happen then @@ -3105,7 +3108,9 @@ func CheckPackageErrors(pkgs []*Package) { } seen[key] = true } - base.ExitIfErrors() + if len(reported) > 0 { + base.ExitIfErrors() + } } // mainPackagesOnly filters out non-main packages matched only by arguments @@ -3555,14 +3560,6 @@ func SelectCoverPackages(roots []*Package, match []func(*Package) bool, op strin if cfg.BuildCoverMode == "atomic" { EnsureImport(p, "sync/atomic") } - - // Generate covervars if using legacy coverage design. - if !cfg.Experiment.CoverageRedesign { - var coverFiles []string - coverFiles = append(coverFiles, p.GoFiles...) - coverFiles = append(coverFiles, p.CgoFiles...) - p.Internal.CoverVars = DeclareCoverVars(p, coverFiles...) - } } // Warn about -coverpkg arguments that are not actually used. @@ -3574,42 +3571,3 @@ func SelectCoverPackages(roots []*Package, match []func(*Package) bool, op strin return covered } - -// DeclareCoverVars attaches the required cover variables names -// to the files, to be used when annotating the files. This -// function only called when using legacy coverage test/build -// (e.g. GOEXPERIMENT=coverageredesign is off). -func DeclareCoverVars(p *Package, files ...string) map[string]*CoverVar { - coverVars := make(map[string]*CoverVar) - coverIndex := 0 - // We create the cover counters as new top-level variables in the package. - // We need to avoid collisions with user variables (GoCover_0 is unlikely but still) - // and more importantly with dot imports of other covered packages, - // so we append 12 hex digits from the SHA-256 of the import path. - // The point is only to avoid accidents, not to defeat users determined to - // break things. - sum := sha256.Sum256([]byte(p.ImportPath)) - h := fmt.Sprintf("%x", sum[:6]) - for _, file := range files { - if base.IsTestFile(file) { - continue - } - // For a package that is "local" (imported via ./ import or command line, outside GOPATH), - // we record the full path to the file name. - // Otherwise we record the import path, then a forward slash, then the file name. - // This makes profiles within GOPATH file system-independent. - // These names appear in the cmd/cover HTML interface. - var longFile string - if p.Internal.Local { - longFile = filepath.Join(p.Dir, file) - } else { - longFile = pathpkg.Join(p.ImportPath, file) - } - coverVars[file] = &CoverVar{ - File: longFile, - Var: fmt.Sprintf("GoCover_%d_%x", coverIndex, h), - } - coverIndex++ - } - return coverVars -} diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go index ddd14a03046016..f895e3a2461d9e 100644 --- a/src/cmd/go/internal/load/test.go +++ b/src/cmd/go/internal/load/test.go @@ -23,7 +23,6 @@ import ( "unicode" "unicode/utf8" - "cmd/go/internal/cfg" "cmd/go/internal/fsys" "cmd/go/internal/str" "cmd/go/internal/trace" @@ -42,7 +41,6 @@ type TestCover struct { Local bool Pkgs []*Package Paths []string - Vars []coverInfo } // TestPackagesFor is like TestPackagesAndErrors but it returns @@ -309,7 +307,7 @@ func TestPackagesAndErrors(ctx context.Context, done func(), opts PackageOpts, p // Also the linker introduces implicit dependencies reported by LinkerDeps. stk.Push(ImportInfo{Pkg: "testmain"}) deps := TestMainDeps // cap==len, so safe for append - if cover != nil && cfg.Experiment.CoverageRedesign { + if cover != nil { deps = append(deps, "internal/coverage/cfile") } ldDeps, err := LinkerDeps(p) @@ -334,22 +332,6 @@ func TestPackagesAndErrors(ctx context.Context, done func(), opts PackageOpts, p stk.Pop() parallelizablePart := func() { - if cover != nil && cover.Pkgs != nil && !cfg.Experiment.CoverageRedesign { - // Add imports, but avoid duplicates. - seen := map[*Package]bool{p: true, ptest: true} - for _, p1 := range pmain.Internal.Imports { - seen[p1] = true - } - for _, p1 := range cover.Pkgs { - if seen[p1] { - // Don't add duplicate imports. - continue - } - seen[p1] = true - pmain.Internal.Imports = append(pmain.Internal.Imports, p1) - } - } - allTestImports := make([]*Package, 0, len(pmain.Internal.Imports)+len(imports)+len(ximports)) allTestImports = append(allTestImports, pmain.Internal.Imports...) allTestImports = append(allTestImports, imports...) @@ -397,35 +379,19 @@ func TestPackagesAndErrors(ctx context.Context, done func(), opts PackageOpts, p } if cover != nil { - if cfg.Experiment.CoverageRedesign { - // Here ptest needs to inherit the proper coverage mode (since - // it contains p's Go files), whereas pmain contains only - // test harness code (don't want to instrument it, and - // we don't want coverage hooks in the pkg init). - ptest.Internal.Cover.Mode = p.Internal.Cover.Mode - pmain.Internal.Cover.Mode = "testmain" - } + // Here ptest needs to inherit the proper coverage mode (since + // it contains p's Go files), whereas pmain contains only + // test harness code (don't want to instrument it, and + // we don't want coverage hooks in the pkg init). + ptest.Internal.Cover.Mode = p.Internal.Cover.Mode + pmain.Internal.Cover.Mode = "testmain" + // Should we apply coverage analysis locally, only for this // package and only for this test? Yes, if -cover is on but // -coverpkg has not specified a list of packages for global // coverage. if cover.Local { ptest.Internal.Cover.Mode = cover.Mode - - if !cfg.Experiment.CoverageRedesign { - var coverFiles []string - coverFiles = append(coverFiles, ptest.GoFiles...) - coverFiles = append(coverFiles, ptest.CgoFiles...) - ptest.Internal.CoverVars = DeclareCoverVars(ptest, coverFiles...) - } - } - - if !cfg.Experiment.CoverageRedesign { - for _, cp := range pmain.Internal.Imports { - if len(cp.Internal.CoverVars) > 0 { - t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars}) - } - } } } @@ -625,11 +591,6 @@ func isTest(name, prefix string) bool { return !unicode.IsLower(rune) } -type coverInfo struct { - Package *Package - Vars map[string]*CoverVar -} - // loadTestFuncs returns the testFuncs describing the tests that will be run. // The returned testFuncs is always non-nil, even if an error occurred while // processing test files. @@ -655,9 +616,6 @@ func loadTestFuncs(ptest *Package) (*testFuncs, error) { func formatTestmain(t *testFuncs) ([]byte, error) { var buf bytes.Buffer tmpl := testmainTmpl - if cfg.Experiment.CoverageRedesign { - tmpl = testmainTmplNewCoverage - } if err := tmpl.Execute(&buf, t); err != nil { return nil, err } @@ -825,119 +783,6 @@ var testmainTmpl = lazytemplate.New("main", ` package main -import ( - "os" -{{if .TestMain}} - "reflect" -{{end}} - "testing" - "testing/internal/testdeps" - -{{if .ImportTest}} - {{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}} -{{end}} -{{if .ImportXtest}} - {{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}} -{{end}} -{{if .Cover}} -{{range $i, $p := .Cover.Vars}} - _cover{{$i}} {{$p.Package.ImportPath | printf "%q"}} -{{end}} -{{end}} -) - -var tests = []testing.InternalTest{ -{{range .Tests}} - {"{{.Name}}", {{.Package}}.{{.Name}}}, -{{end}} -} - -var benchmarks = []testing.InternalBenchmark{ -{{range .Benchmarks}} - {"{{.Name}}", {{.Package}}.{{.Name}}}, -{{end}} -} - -var fuzzTargets = []testing.InternalFuzzTarget{ -{{range .FuzzTargets}} - {"{{.Name}}", {{.Package}}.{{.Name}}}, -{{end}} -} - -var examples = []testing.InternalExample{ -{{range .Examples}} - {"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}}, -{{end}} -} - -func init() { - testdeps.ImportPath = {{.ImportPath | printf "%q"}} -} - -{{if .Cover}} - -// Only updated by init functions, so no need for atomicity. -var ( - coverCounters = make(map[string][]uint32) - coverBlocks = make(map[string][]testing.CoverBlock) -) - -func init() { - {{range $i, $p := .Cover.Vars}} - {{range $file, $cover := $p.Vars}} - coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:]) - {{end}} - {{end}} -} - -func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) { - if 3*len(counter) != len(pos) || len(counter) != len(numStmts) { - panic("coverage: mismatched sizes") - } - if coverCounters[fileName] != nil { - // Already registered. - return - } - coverCounters[fileName] = counter - block := make([]testing.CoverBlock, len(counter)) - for i := range counter { - block[i] = testing.CoverBlock{ - Line0: pos[3*i+0], - Col0: uint16(pos[3*i+2]), - Line1: pos[3*i+1], - Col1: uint16(pos[3*i+2]>>16), - Stmts: numStmts[i], - } - } - coverBlocks[fileName] = block -} -{{end}} - -func main() { -{{if .Cover}} - testing.RegisterCover(testing.Cover{ - Mode: {{printf "%q" .Cover.Mode}}, - Counters: coverCounters, - Blocks: coverBlocks, - CoveredPackages: {{printf "%q" .Covered}}, - }) -{{end}} - m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, fuzzTargets, examples) -{{with .TestMain}} - {{.Package}}.{{.Name}}(m) - os.Exit(int(reflect.ValueOf(m).Elem().FieldByName("exitCode").Int())) -{{else}} - os.Exit(m.Run()) -{{end}} -} - -`) - -var testmainTmplNewCoverage = lazytemplate.New("main", ` -// Code generated by 'go test'. DO NOT EDIT. - -package main - import ( "os" {{if .TestMain}} diff --git a/src/cmd/go/internal/lockedfile/lockedfile.go b/src/cmd/go/internal/lockedfile/lockedfile.go index 82e1a89675e704..8bd2ffbe8f7860 100644 --- a/src/cmd/go/internal/lockedfile/lockedfile.go +++ b/src/cmd/go/internal/lockedfile/lockedfile.go @@ -24,6 +24,8 @@ import ( type File struct { osFile closed bool + // cleanup panics when the file is no longer referenced and it has not been closed. + cleanup runtime.Cleanup } // osFile embeds a *os.File while keeping the pointer itself unexported. @@ -48,11 +50,11 @@ func OpenFile(name string, flag int, perm fs.FileMode) (*File, error) { // Although the operating system will drop locks for open files when the go // command exits, we want to hold locks for as little time as possible, and we // especially don't want to leave a file locked after we're done with it. Our - // Close method is what releases the locks, so use a finalizer to report + // Close method is what releases the locks, so use a cleanup to report // missing Close calls on a best-effort basis. - runtime.SetFinalizer(f, func(f *File) { - panic(fmt.Sprintf("lockedfile.File %s became unreachable without a call to Close", f.Name())) - }) + f.cleanup = runtime.AddCleanup(f, func(fileName string) { + panic(fmt.Sprintf("lockedfile.File %s became unreachable without a call to Close", fileName)) + }, f.Name()) return f, nil } @@ -91,7 +93,7 @@ func (f *File) Close() error { f.closed = true err := closeFile(f.osFile.File) - runtime.SetFinalizer(f, nil) + f.cleanup.Stop() return err } diff --git a/src/cmd/go/internal/mmap/mmap.go b/src/cmd/go/internal/mmap/mmap.go index fcbd3e08c1c517..fd374df82efa1f 100644 --- a/src/cmd/go/internal/mmap/mmap.go +++ b/src/cmd/go/internal/mmap/mmap.go @@ -22,10 +22,11 @@ type Data struct { } // Mmap maps the given file into memory. -func Mmap(file string) (Data, error) { +func Mmap(file string) (Data, bool, error) { f, err := os.Open(file) if err != nil { - return Data{}, err + return Data{}, false, err } - return mmapFile(f) + data, err := mmapFile(f) + return data, true, err } diff --git a/src/cmd/go/internal/mmap/mmap_test.go b/src/cmd/go/internal/mmap/mmap_test.go new file mode 100644 index 00000000000000..3f4b63caf19bd2 --- /dev/null +++ b/src/cmd/go/internal/mmap/mmap_test.go @@ -0,0 +1,32 @@ +// Copyright 2025 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. + +package mmap + +import ( + "bytes" + "testing" +) + +// TestMmap does a round trip to make sure the slice returned by +// mmap contains the same data as was written to the file. It's +// a test on one of the issues in #71059: on Windows we were +// returning a slice containing all the data in the mmapped pages, +// which could be longer than the file. +func TestMmap(t *testing.T) { + // Use an already existing file as our test data. Avoid creating + // a temporary file so that we don't have to close the mapping on + // Windows before deleting the file during test cleanup. + f := "testdata/small_file.txt" + + want := []byte("This file is shorter than 4096 bytes.\n") + + data, _, err := Mmap(f) + if err != nil { + t.Fatalf("calling Mmap: %v", err) + } + if !bytes.Equal(data.Data, want) { + t.Fatalf("mmapped data slice: got %q; want %q", data.Data, want) + } +} diff --git a/src/cmd/go/internal/mmap/mmap_windows.go b/src/cmd/go/internal/mmap/mmap_windows.go index d00bef71e5cfc5..4163484b1a3510 100644 --- a/src/cmd/go/internal/mmap/mmap_windows.go +++ b/src/cmd/go/internal/mmap/mmap_windows.go @@ -37,5 +37,11 @@ func mmapFile(f *os.File) (Data, error) { return Data{}, fmt.Errorf("VirtualQuery %s: %w", f.Name(), err) } data := unsafe.Slice((*byte)(unsafe.Pointer(addr)), int(info.RegionSize)) - return Data{f, data}, nil + if len(data) < int(size) { + // In some cases, especially on 386, we may not receive a in incomplete mapping: + // one that is shorter than the file itself. Return an error in those cases because + // incomplete mappings are not useful. + return Data{}, fmt.Errorf("mmapFile: received incomplete mapping of file") + } + return Data{f, data[:int(size)]}, nil } diff --git a/src/cmd/go/internal/mmap/testdata/small_file.txt b/src/cmd/go/internal/mmap/testdata/small_file.txt new file mode 100644 index 00000000000000..10bb609f2ab334 --- /dev/null +++ b/src/cmd/go/internal/mmap/testdata/small_file.txt @@ -0,0 +1 @@ +This file is shorter than 4096 bytes. diff --git a/src/cmd/go/internal/modfetch/cache.go b/src/cmd/go/internal/modfetch/cache.go index 02d3849314a75a..9c34581a910a4a 100644 --- a/src/cmd/go/internal/modfetch/cache.go +++ b/src/cmd/go/internal/modfetch/cache.go @@ -113,6 +113,13 @@ func DownloadDir(ctx context.Context, m module.Version) (string, error) { return dir, err } + // Special case: ziphash is not required for the golang.org/fips140 module, + // because it is unpacked from a file in GOROOT, not downloaded. + // We've already checked that it's not a partial unpacking, so we're happy. + if m.Path == "golang.org/fips140" { + return dir, nil + } + // Check if a .ziphash file exists. It should be created before the // zip is extracted, but if it was deleted (by another program?), we need // to re-calculate it. Note that checkMod will repopulate the ziphash diff --git a/src/cmd/go/internal/modfetch/codehost/git.go b/src/cmd/go/internal/modfetch/codehost/git.go index 50a4526eb3ca64..dfb366788967f7 100644 --- a/src/cmd/go/internal/modfetch/codehost/git.go +++ b/src/cmd/go/internal/modfetch/codehost/git.go @@ -649,7 +649,21 @@ func (r *gitRepo) statLocal(ctx context.Context, version, rev string) (*RevInfo, } } } - sort.Strings(info.Tags) + + // Git 2.47.1 does not send the tags during shallow clone anymore + // (perhaps the exact version that changed behavior is an earlier one), + // so we have to also add tags from the refs list we fetched with ls-remote. + if refs, err := r.loadRefs(ctx); err == nil { + for ref, h := range refs { + if h == hash { + if tag, found := strings.CutPrefix(ref, "refs/tags/"); found { + info.Tags = append(info.Tags, tag) + } + } + } + } + slices.Sort(info.Tags) + info.Tags = slices.Compact(info.Tags) // Used hash as info.Version above. // Use caller's suggested version if it appears in the tag list diff --git a/src/cmd/go/internal/modfetch/proxy.go b/src/cmd/go/internal/modfetch/proxy.go index e0efb097ecdda9..896f310bdf77ca 100644 --- a/src/cmd/go/internal/modfetch/proxy.go +++ b/src/cmd/go/internal/modfetch/proxy.go @@ -12,7 +12,6 @@ import ( "io" "io/fs" "net/url" - "path" pathpkg "path" "path/filepath" "strings" @@ -98,7 +97,7 @@ func proxyList() ([]proxySpec, error) { // Single-word tokens are reserved for built-in behaviors, and anything // containing the string ":/" or matching an absolute file path must be a // complete URL. For all other paths, implicitly add "https://". - if strings.ContainsAny(url, ".:/") && !strings.Contains(url, ":/") && !filepath.IsAbs(url) && !path.IsAbs(url) { + if strings.ContainsAny(url, ".:/") && !strings.Contains(url, ":/") && !filepath.IsAbs(url) && !pathpkg.IsAbs(url) { url = "https://" + url } diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go index 159a8569114207..48ae12fe53a783 100644 --- a/src/cmd/go/internal/modget/get.go +++ b/src/cmd/go/internal/modget/get.go @@ -125,11 +125,6 @@ suggested Go toolchain, see https://go.dev/doc/toolchain. For more about specifying packages, see 'go help packages'. -This text describes the behavior of get using modules to manage source -code and dependencies. If instead the go command is running in GOPATH -mode, the details of get's flags and effects change, as does 'go help get'. -See 'go help gopath-get'. - See also: go build, go install, go clean, go mod. `, } diff --git a/src/cmd/go/internal/modindex/build.go b/src/cmd/go/internal/modindex/build.go index b4dacb0f523a8d..d7e09fed25f43a 100644 --- a/src/cmd/go/internal/modindex/build.go +++ b/src/cmd/go/internal/modindex/build.go @@ -21,6 +21,7 @@ import ( "io" "io/fs" "path/filepath" + "slices" "sort" "strings" "unicode" @@ -133,7 +134,7 @@ func (ctxt *Context) isAbsPath(path string) bool { return filepath.IsAbs(path) } -// isDir calls ctxt.IsDir (if not nil) or else uses fsys.Stat. +// isDir reports whether path is a directory. func isDir(path string) bool { fi, err := fsys.Stat(path) return err == nil && fi.IsDir() @@ -887,23 +888,8 @@ func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool { } // other tags - for _, tag := range ctxt.BuildTags { - if tag == name { - return true - } - } - for _, tag := range ctxt.ToolTags { - if tag == name { - return true - } - } - for _, tag := range ctxt.ReleaseTags { - if tag == name { - return true - } - } - - return false + return slices.Contains(ctxt.BuildTags, name) || slices.Contains(ctxt.ToolTags, name) || + slices.Contains(ctxt.ReleaseTags, name) } // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH diff --git a/src/cmd/go/internal/modindex/read.go b/src/cmd/go/internal/modindex/read.go index c4102409b433f3..76216f35ba149f 100644 --- a/src/cmd/go/internal/modindex/read.go +++ b/src/cmd/go/internal/modindex/read.go @@ -33,10 +33,7 @@ import ( "cmd/internal/par" ) -// enabled is used to flag off the behavior of the module index on tip. -// It will be removed before the release. -// TODO(matloob): Remove enabled once we have more confidence on the -// module index. +// enabled is used to flag off the behavior of the module index on tip, for debugging. var enabled = godebug.New("#goindex").Value() != "0" // Module represents and encoded module index file. It is used to @@ -126,6 +123,7 @@ var ErrNotIndexed = errors.New("not in module index") var ( errDisabled = fmt.Errorf("%w: module indexing disabled", ErrNotIndexed) errNotFromModuleCache = fmt.Errorf("%w: not from module cache", ErrNotIndexed) + errFIPS140 = fmt.Errorf("%w: fips140 snapshots not indexed", ErrNotIndexed) ) // GetPackage returns the IndexPackage for the directory at the given path. @@ -143,6 +141,11 @@ func GetPackage(modroot, pkgdir string) (*IndexPackage, error) { if cfg.BuildContext.Compiler == "gccgo" && str.HasPathPrefix(modroot, cfg.GOROOTsrc) { return nil, err // gccgo has no sources for GOROOT packages. } + // The pkgdir for fips140 has been replaced in the fsys overlay, + // but the module index does not see that. Do not try to use the module index. + if strings.Contains(filepath.ToSlash(pkgdir), "internal/fips140/v") { + return nil, errFIPS140 + } return openIndexPackage(modroot, pkgdir) } @@ -183,16 +186,21 @@ func openIndexModule(modroot string, ismodcache bool) (*Module, error) { if err != nil { return nil, err } - data, _, err := cache.GetMmap(cache.Default(), id) + data, _, opened, err := cache.GetMmap(cache.Default(), id) if err != nil { // Couldn't read from modindex. Assume we couldn't read from // the index because the module hasn't been indexed yet. + // But double check on Windows that we haven't opened the file yet, + // because once mmap opens the file, we can't close it, and + // Windows won't let us open an already opened file. data, err = indexModule(modroot) if err != nil { return nil, err } - if err = cache.PutBytes(cache.Default(), id, data); err != nil { - return nil, err + if runtime.GOOS != "windows" || !opened { + if err = cache.PutBytes(cache.Default(), id, data); err != nil { + return nil, err + } } } mi, err := fromBytes(modroot, data) @@ -212,13 +220,18 @@ func openIndexPackage(modroot, pkgdir string) (*IndexPackage, error) { if err != nil { return nil, err } - data, _, err := cache.GetMmap(cache.Default(), id) + data, _, opened, err := cache.GetMmap(cache.Default(), id) if err != nil { // Couldn't read from index. Assume we couldn't read from // the index because the package hasn't been indexed yet. + // But double check on Windows that we haven't opened the file yet, + // because once mmap opens the file, we can't close it, and + // Windows won't let us open an already opened file. data = indexPackage(modroot, pkgdir) - if err = cache.PutBytes(cache.Default(), id, data); err != nil { - return nil, err + if runtime.GOOS != "windows" || !opened { + if err = cache.PutBytes(cache.Default(), id, data); err != nil { + return nil, err + } } } pkg, err := packageFromBytes(modroot, data) diff --git a/src/cmd/go/internal/modload/init.go b/src/cmd/go/internal/modload/init.go index 8fdcd0da6328f6..5d01aedc2f3f8e 100644 --- a/src/cmd/go/internal/modload/init.go +++ b/src/cmd/go/internal/modload/init.go @@ -452,23 +452,6 @@ func Init() { os.Setenv("GIT_TERMINAL_PROMPT", "0") } - // Disable any ssh connection pooling by Git. - // If a Git subprocess forks a child into the background to cache a new connection, - // that child keeps stdout/stderr open. After the Git subprocess exits, - // os/exec expects to be able to read from the stdout/stderr pipe - // until EOF to get all the data that the Git subprocess wrote before exiting. - // The EOF doesn't come until the child exits too, because the child - // is holding the write end of the pipe. - // This is unfortunate, but it has come up at least twice - // (see golang.org/issue/13453 and golang.org/issue/16104) - // and confuses users when it does. - // If the user has explicitly set GIT_SSH or GIT_SSH_COMMAND, - // assume they know what they are doing and don't step on it. - // But default to turning off ControlMaster. - if os.Getenv("GIT_SSH") == "" && os.Getenv("GIT_SSH_COMMAND") == "" { - os.Setenv("GIT_SSH_COMMAND", "ssh -o ControlMaster=no -o BatchMode=yes") - } - if os.Getenv("GCM_INTERACTIVE") == "" { os.Setenv("GCM_INTERACTIVE", "never") } diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go index 12dd9425f65962..1a3a4b5a694493 100644 --- a/src/cmd/go/internal/modload/load.go +++ b/src/cmd/go/internal/modload/load.go @@ -103,7 +103,6 @@ import ( "io/fs" "maps" "os" - "path" pathpkg "path" "path/filepath" "runtime" @@ -717,7 +716,7 @@ func pathInModuleCache(ctx context.Context, dir string, rs *Requirements) string return "", false } - return path.Join(m.Path, filepath.ToSlash(sub)), true + return pathpkg.Join(m.Path, filepath.ToSlash(sub)), true } if rs.pruning == pruned { diff --git a/src/cmd/go/internal/modload/modfile.go b/src/cmd/go/internal/modload/modfile.go index 94d2f5bd666cab..4687deae686c89 100644 --- a/src/cmd/go/internal/modload/modfile.go +++ b/src/cmd/go/internal/modload/modfile.go @@ -44,6 +44,17 @@ func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfil f, err = modfile.Parse(gomod, data, fix) if err != nil { + f, laxErr := modfile.ParseLax(gomod, data, fix) + if laxErr == nil { + if f.Go != nil && gover.Compare(f.Go.Version, gover.Local()) > 0 { + toolchain := "" + if f.Toolchain != nil { + toolchain = f.Toolchain.Name + } + return nil, nil, &gover.TooNewError{What: base.ShortPath(gomod), GoVersion: f.Go.Version, Toolchain: toolchain} + } + } + // Errors returned by modfile.Parse begin with file:line. return nil, nil, fmt.Errorf("errors parsing %s:\n%w", base.ShortPath(gomod), shortPathErrorList(err)) } diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go index 5067cb28354fd8..b81b1a007bd79d 100644 --- a/src/cmd/go/internal/run/run.go +++ b/src/cmd/go/internal/run/run.go @@ -66,9 +66,7 @@ func init() { CmdRun.Run = runRun // break init loop work.AddBuildFlags(CmdRun, work.DefaultBuildFlags) - if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign { - work.AddCoverFlags(CmdRun, nil) - } + work.AddCoverFlags(CmdRun, nil) CmdRun.Flag.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "") } @@ -141,7 +139,7 @@ func runRun(ctx context.Context, cmd *base.Command, args []string) { cmdArgs := args[i:] load.CheckPackageErrors([]*load.Package{p}) - if cfg.Experiment.CoverageRedesign && cfg.BuildCover { + if cfg.BuildCover { load.PrepareForCoverageBuild([]*load.Package{p}) } diff --git a/src/cmd/go/internal/test/test.go b/src/cmd/go/internal/test/test.go index 41ddb2f5d01e77..2ee2aa6f415d5c 100644 --- a/src/cmd/go/internal/test/test.go +++ b/src/cmd/go/internal/test/test.go @@ -127,7 +127,7 @@ elapsed time in the summary line. The rule for a match in the cache is that the run involves the same test binary and the flags on the command line come entirely from a restricted set of 'cacheable' test flags, defined as -benchtime, -cpu, --list, -parallel, -run, -short, -timeout, -failfast, -fullpath and -v. +-list, -parallel, -run, -short, -skip, -timeout, -failfast, -fullpath and -v. If a run of go test has any test or non-test flags outside this set, the result is not cached. To disable test caching, use any test flag or argument other than the cacheable flags. The idiomatic way to disable @@ -802,9 +802,9 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { // to that timeout plus one minute. This is a backup alarm in case // the test wedges with a goroutine spinning and its background // timer does not get a chance to fire. - // Don't set this if fuzzing, since it should be able to run + // Don't set this if fuzzing or benchmarking, since it should be able to run // indefinitely. - if testTimeout > 0 && testFuzz == "" { + if testTimeout > 0 && testFuzz == "" && testBench == "" { // The WaitDelay for the test process depends on both the OS I/O and // scheduling overhead and the amount of I/O generated by the test just // before it exits. We set the minimum at 5 seconds to account for the OS @@ -867,7 +867,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { // patterns. plist := load.TestPackageList(ctx, pkgOpts, pkgs) testCoverPkgs = load.SelectCoverPackages(plist, match, "test") - if cfg.Experiment.CoverageRedesign && len(testCoverPkgs) > 0 { + if len(testCoverPkgs) > 0 { // create a new singleton action that will collect up the // meta-data files from all of the packages mentioned in // "-coverpkg" and write them to a summary file. This new @@ -984,9 +984,7 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { // later package. Note that if -coverpkg is in effect // p.Internal.Cover.GenMeta will wind up being set for // all matching packages. - if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 && - cfg.BuildCoverPkg == nil && - cfg.Experiment.CoverageRedesign { + if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 && cfg.BuildCoverPkg == nil { p.Internal.Cover.GenMeta = true } } @@ -994,14 +992,15 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { // Prepare build + run + print actions for all packages being tested. for _, p := range pkgs { - buildTest, runTest, printTest, perr, err := builderTest(b, ctx, pkgOpts, p, allImports[p], writeCoverMetaAct) - if err != nil { + reportErr := func(perr *load.Package, err error) { str := err.Error() if p.ImportPath != "" { load.DefaultPrinter().Errorf(perr, "# %s\n%s", p.ImportPath, str) } else { load.DefaultPrinter().Errorf(perr, "%s", str) } + } + reportSetupFailed := func(perr *load.Package, err error) { var stdout io.Writer = os.Stdout if testJSON { json := test2json.NewConverter(stdout, p.ImportPath, test2json.Timestamp) @@ -1009,11 +1008,34 @@ func runTest(ctx context.Context, cmd *base.Command, args []string) { json.Exited(err) json.Close() }() - json.SetFailedBuild(perr.Desc()) + if gotestjsonbuildtext.Value() == "1" { + // While this flag is about go build -json, the other effect + // of that change was to include "FailedBuild" in the test JSON. + gotestjsonbuildtext.IncNonDefault() + } else { + json.SetFailedBuild(perr.Desc()) + } stdout = json } fmt.Fprintf(stdout, "FAIL\t%s [setup failed]\n", p.ImportPath) base.SetExitStatus(1) + } + + var firstErrPkg *load.Package // arbitrarily report setup failed error for first error pkg reached in DFS + load.PackageErrors([]*load.Package{p}, func(p *load.Package) { + reportErr(p, p.Error) + if firstErrPkg == nil { + firstErrPkg = p + } + }) + if firstErrPkg != nil { + reportSetupFailed(firstErrPkg, firstErrPkg.Error) + continue + } + buildTest, runTest, printTest, perr, err := builderTest(b, ctx, pkgOpts, p, allImports[p], writeCoverMetaAct) + if err != nil { + reportErr(perr, err) + reportSetupFailed(perr, err) continue } builds = append(builds, buildTest) @@ -1068,7 +1090,7 @@ var windowsBadWords = []string{ func builderTest(b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package, imported bool, writeCoverMetaAct *work.Action) (buildAction, runAction, printAction *work.Action, perr *load.Package, err error) { if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { - if cfg.BuildCover && cfg.Experiment.CoverageRedesign { + if cfg.BuildCover { if p.Internal.Cover.GenMeta { p.Internal.Cover.Mode = cfg.BuildCoverMode } @@ -1437,7 +1459,11 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action) if a.Failed != nil { // We were unable to build the binary. if json != nil && a.Failed.Package != nil { - json.SetFailedBuild(a.Failed.Package.Desc()) + if gotestjsonbuildtext.Value() == "1" { + gotestjsonbuildtext.IncNonDefault() + } else { + json.SetFailedBuild(a.Failed.Package.Desc()) + } } a.Failed = nil fmt.Fprintf(stdout, "FAIL\t%s [build failed]\n", a.Package.ImportPath) @@ -1459,7 +1485,7 @@ func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action) if p := a.Package; len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { reportNoTestFiles := true - if cfg.BuildCover && cfg.Experiment.CoverageRedesign && p.Internal.Cover.GenMeta { + if cfg.BuildCover && p.Internal.Cover.GenMeta { if err := sh.Mkdir(a.Objdir); err != nil { return err } @@ -1755,6 +1781,7 @@ func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bo "-test.parallel", "-test.run", "-test.short", + "-test.skip", "-test.timeout", "-test.failfast", "-test.v", diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go index 64c40adab2baa9..7033eb1d9c3587 100644 --- a/src/cmd/go/internal/tool/tool.go +++ b/src/cmd/go/internal/tool/tool.go @@ -46,6 +46,13 @@ With no arguments it prints the list of known tools. The -n flag causes tool to print the command that would be executed but not execute it. +The -modfile=file.mod build flag causes tool to use an alternate file +instead of the go.mod in the module root directory. + +Tool also provides the -C, -overlay, and -modcacherw build flags. + +For more about build flags, see 'go help build'. + For more about each builtin tool command, see 'go doc cmd/'. `, } diff --git a/src/cmd/go/internal/toolchain/select.go b/src/cmd/go/internal/toolchain/select.go index cbdd7a2418e1f0..aeab59519c7e6d 100644 --- a/src/cmd/go/internal/toolchain/select.go +++ b/src/cmd/go/internal/toolchain/select.go @@ -169,7 +169,7 @@ func Select() { } gotoolchain = minToolchain - if (mode == "auto" || mode == "path") && !goInstallVersion() { + if (mode == "auto" || mode == "path") && !goInstallVersion(minVers) { // Read go.mod to find new minimum and suggested toolchain. file, goVers, toolchain := modGoToolchain() gover.Startup.AutoFile = file @@ -549,7 +549,7 @@ func modGoToolchain() (file, goVers, toolchain string) { // goInstallVersion reports whether the command line is go install m@v or go run m@v. // If so, Select must not read the go.mod or go.work file in "auto" or "path" mode. -func goInstallVersion() bool { +func goInstallVersion(minVers string) bool { // Note: We assume there are no flags between 'go' and 'install' or 'run'. // During testing there are some debugging flags that are accepted // in that position, but in production go binaries there are not. @@ -708,7 +708,11 @@ func goInstallVersion() bool { if errors.Is(err, gover.ErrTooNew) { // Run early switch, same one go install or go run would eventually do, // if it understood all the command-line flags. - SwitchOrFatal(ctx, err) + var s Switcher + s.Error(err) + if s.TooNew != nil && gover.Compare(s.TooNew.GoVersion, minVers) > 0 { + SwitchOrFatal(ctx, err) + } } return true // pkg@version found diff --git a/src/cmd/go/internal/vcweb/auth.go b/src/cmd/go/internal/vcweb/auth.go index 383bf759ffcdc1..e7c7c6ca265758 100644 --- a/src/cmd/go/internal/vcweb/auth.go +++ b/src/cmd/go/internal/vcweb/auth.go @@ -63,6 +63,7 @@ func (h *authHandler) Handler(dir string, env []string, logger *log.Logger) (htt var err error accessFile, err = fs.Open(path.Join(accessDir, ".access")) if err == nil { + defer accessFile.Close() break } diff --git a/src/cmd/go/internal/vcweb/script.go b/src/cmd/go/internal/vcweb/script.go index 1ba9c0aff4f36a..3342ab200c6faa 100644 --- a/src/cmd/go/internal/vcweb/script.go +++ b/src/cmd/go/internal/vcweb/script.go @@ -32,6 +32,17 @@ import ( func newScriptEngine() *script.Engine { conds := script.DefaultConds() + add := func(name string, cond script.Cond) { + if _, ok := conds[name]; ok { + panic(fmt.Sprintf("condition %q is already registered", name)) + } + conds[name] = cond + } + lazyBool := func(summary string, f func() bool) script.Cond { + return script.OnceCondition(summary, func() (bool, error) { return f(), nil }) + } + add("bzr", lazyBool("the 'bzr' executable exists and provides the standard CLI", hasWorkingBzr)) + interrupt := func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) } gracePeriod := 30 * time.Second // arbitrary @@ -43,6 +54,7 @@ func newScriptEngine() *script.Engine { cmds["hg"] = script.Program("hg", interrupt, gracePeriod) cmds["handle"] = scriptHandle() cmds["modzip"] = scriptModzip() + cmds["skip"] = scriptSkip() cmds["svnadmin"] = script.Program("svnadmin", interrupt, gracePeriod) cmds["svn"] = script.Program("svn", interrupt, gracePeriod) cmds["unquote"] = scriptUnquote() @@ -321,6 +333,34 @@ func scriptModzip() script.Cmd { }) } +func scriptSkip() script.Cmd { + return script.Command( + script.CmdUsage{ + Summary: "skip the current test", + Args: "[msg]", + }, + func(_ *script.State, args ...string) (script.WaitFunc, error) { + if len(args) > 1 { + return nil, script.ErrUsage + } + if len(args) == 0 { + return nil, SkipError{""} + } + return nil, SkipError{args[0]} + }) +} + +type SkipError struct { + Msg string +} + +func (s SkipError) Error() string { + if s.Msg == "" { + return "skip" + } + return s.Msg +} + func scriptUnquote() script.Cmd { return script.Command( script.CmdUsage{ @@ -343,3 +383,14 @@ func scriptUnquote() script.Cmd { return wait, nil }) } + +func hasWorkingBzr() bool { + bzr, err := exec.LookPath("bzr") + if err != nil { + return false + } + // Check that 'bzr help' exits with code 0. + // See go.dev/issue/71504 for an example where 'bzr' exists in PATH but doesn't work. + err = exec.Command(bzr, "help").Run() + return err == nil +} diff --git a/src/cmd/go/internal/vcweb/vcstest/vcstest_test.go b/src/cmd/go/internal/vcweb/vcstest/vcstest_test.go index 4a6d60039ed0b2..67234ac20d4628 100644 --- a/src/cmd/go/internal/vcweb/vcstest/vcstest_test.go +++ b/src/cmd/go/internal/vcweb/vcstest/vcstest_test.go @@ -158,6 +158,13 @@ func TestScripts(t *testing.T) { if notInstalled := (vcweb.ServerNotInstalledError{}); errors.As(err, ¬Installed) || errors.Is(err, exec.ErrNotFound) { t.Skip(err) } + if skip := (vcweb.SkipError{}); errors.As(err, &skip) { + if skip.Msg == "" { + t.Skip("SKIP") + } else { + t.Skipf("SKIP: %v", skip.Msg) + } + } t.Error(err) } }) diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go index 3508d51fbb0452..873feb8a2604ae 100644 --- a/src/cmd/go/internal/work/build.go +++ b/src/cmd/go/internal/work/build.go @@ -247,10 +247,8 @@ func init() { AddBuildFlags(CmdBuild, DefaultBuildFlags) AddBuildFlags(CmdInstall, DefaultBuildFlags) - if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign { - AddCoverFlags(CmdBuild, nil) - AddCoverFlags(CmdInstall, nil) - } + AddCoverFlags(CmdBuild, nil) + AddCoverFlags(CmdInstall, nil) } // Note that flags consulted by other parts of the code @@ -361,26 +359,13 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) { cmd.Flag.StringVar(&cfg.DebugTrace, "debug-trace", "", "") } -// AddCoverFlags adds coverage-related flags to "cmd". If the -// CoverageRedesign experiment is enabled, we add -cover{mode,pkg} to -// the build command and only -coverprofile to the test command. If -// the CoverageRedesign experiment is disabled, -cover* flags are -// added only to the test command. +// AddCoverFlags adds coverage-related flags to "cmd". +// We add -cover{mode,pkg} to the build command and only +// -coverprofile to the test command. func AddCoverFlags(cmd *base.Command, coverProfileFlag *string) { - addCover := false - if cfg.Experiment != nil && cfg.Experiment.CoverageRedesign { - // New coverage enabled: both build and test commands get - // coverage flags. - addCover = true - } else { - // New coverage disabled: only test command gets cover flags. - addCover = coverProfileFlag != nil - } - if addCover { - cmd.Flag.BoolVar(&cfg.BuildCover, "cover", false, "") - cmd.Flag.Var(coverFlag{(*coverModeFlag)(&cfg.BuildCoverMode)}, "covermode", "") - cmd.Flag.Var(coverFlag{commaListFlag{&cfg.BuildCoverPkg}}, "coverpkg", "") - } + cmd.Flag.BoolVar(&cfg.BuildCover, "cover", false, "") + cmd.Flag.Var(coverFlag{(*coverModeFlag)(&cfg.BuildCoverMode)}, "covermode", "") + cmd.Flag.Var(coverFlag{commaListFlag{&cfg.BuildCoverPkg}}, "coverpkg", "") if coverProfileFlag != nil { cmd.Flag.Var(coverFlag{V: stringFlag{coverProfileFlag}}, "coverprofile", "") } @@ -515,7 +500,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) { cfg.BuildO = "" } - if cfg.Experiment.CoverageRedesign && cfg.BuildCover { + if cfg.BuildCover { load.PrepareForCoverageBuild(pkgs) } @@ -732,7 +717,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) { } load.CheckPackageErrors(pkgs) - if cfg.Experiment.CoverageRedesign && cfg.BuildCover { + if cfg.BuildCover { load.PrepareForCoverageBuild(pkgs) } diff --git a/src/cmd/go/internal/work/buildid.go b/src/cmd/go/internal/work/buildid.go index 55b3190300ff60..cab722c28a8396 100644 --- a/src/cmd/go/internal/work/buildid.go +++ b/src/cmd/go/internal/work/buildid.go @@ -15,7 +15,6 @@ import ( "cmd/go/internal/base" "cmd/go/internal/cache" "cmd/go/internal/cfg" - "cmd/go/internal/fips140" "cmd/go/internal/fsys" "cmd/go/internal/str" "cmd/internal/buildid" @@ -447,19 +446,6 @@ func (b *Builder) useCache(a *Action, actionHash cache.ActionID, target string, a.buildID = actionID + buildIDSeparator + mainpkg.buildID + buildIDSeparator + contentID } - // In FIPS mode, we disable any link caching, - // so that we always leave fips.o in $WORK/b001. - // This makes sure that labs validating the FIPS - // implementation can always run 'go build -work' - // and then find fips.o in $WORK/b001/fips.o. - // We could instead also save the fips.o and restore it - // to $WORK/b001 from the cache, - // but we went years without caching binaries anyway, - // so not caching them for FIPS will be fine, at least to start. - if a.Mode == "link" && fips140.Enabled() && a.Package != nil && !strings.HasSuffix(a.Package.ImportPath, ".test") { - return false - } - // If user requested -a, we force a rebuild, so don't use the cache. if cfg.BuildA { if p := a.Package; p != nil && !p.Stale { @@ -519,7 +505,7 @@ func (b *Builder) useCache(a *Action, actionHash cache.ActionID, target string, oldBuildID := a.buildID a.buildID = id[1] + buildIDSeparator + id[2] linkID := buildid.HashToString(b.linkActionID(a.triggers[0])) - if id[0] == linkID && !fips140.Enabled() { + if id[0] == linkID { // Best effort attempt to display output from the compile and link steps. // If it doesn't work, it doesn't work: reusing the cached binary is more // important than reprinting diagnostic information. diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go index 2538fae52f2d8e..8d47b8d5cfe3a5 100644 --- a/src/cmd/go/internal/work/exec.go +++ b/src/cmd/go/internal/work/exec.go @@ -301,12 +301,16 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { // compiler changes we rebuild the package. if ccID, _, err := b.gccToolID(ccExe[0], "c"); err == nil { fmt.Fprintf(h, "CC ID=%q\n", ccID) + } else { + fmt.Fprintf(h, "CC ID ERROR=%q\n", err) } if len(p.CXXFiles)+len(p.SwigCXXFiles) > 0 { cxxExe := b.cxxExe() fmt.Fprintf(h, "CXX=%q %q\n", cxxExe, cxxflags) if cxxID, _, err := b.gccToolID(cxxExe[0], "c++"); err == nil { fmt.Fprintf(h, "CXX ID=%q\n", cxxID) + } else { + fmt.Fprintf(h, "CXX ID ERROR=%q\n", err) } } if len(p.FFiles) > 0 { @@ -314,6 +318,8 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID { fmt.Fprintf(h, "FC=%q %q\n", fcExe, fflags) if fcID, _, err := b.gccToolID(fcExe[0], "f95"); err == nil { fmt.Fprintf(h, "FC ID=%q\n", fcID) + } else { + fmt.Fprintf(h, "FC ID ERROR=%q\n", err) } } // TODO(rsc): Should we include the SWIG version? @@ -647,31 +653,18 @@ OverlayLoop: var sourceFile string var coverFile string - var key string if base, found := strings.CutSuffix(file, ".cgo1.go"); found { // cgo files have absolute paths base = filepath.Base(base) sourceFile = file coverFile = objdir + base + ".cgo1.go" - key = base + ".go" } else { sourceFile = filepath.Join(p.Dir, file) coverFile = objdir + file - key = file } coverFile = strings.TrimSuffix(coverFile, ".go") + ".cover.go" - if cfg.Experiment.CoverageRedesign { - infiles = append(infiles, sourceFile) - outfiles = append(outfiles, coverFile) - } else { - cover := p.Internal.CoverVars[key] - if cover == nil { - continue // Not covering this file. - } - if err := b.cover(a, coverFile, sourceFile, cover.Var); err != nil { - return err - } - } + infiles = append(infiles, sourceFile) + outfiles = append(outfiles, coverFile) if i < len(gofiles) { gofiles[i] = coverFile } else { @@ -679,36 +672,27 @@ OverlayLoop: } } - if cfg.Experiment.CoverageRedesign { - if len(infiles) != 0 { - // Coverage instrumentation creates new top level - // variables in the target package for things like - // meta-data containers, counter vars, etc. To avoid - // collisions with user variables, suffix the var name - // with 12 hex digits from the SHA-256 hash of the - // import path. Choice of 12 digits is historical/arbitrary, - // we just need enough of the hash to avoid accidents, - // as opposed to precluding determined attempts by - // users to break things. - sum := sha256.Sum256([]byte(a.Package.ImportPath)) - coverVar := fmt.Sprintf("goCover_%x_", sum[:6]) - mode := a.Package.Internal.Cover.Mode - if mode == "" { - panic("covermode should be set at this point") - } - if newoutfiles, err := b.cover2(a, infiles, outfiles, coverVar, mode); err != nil { - return err - } else { - outfiles = newoutfiles - gofiles = append([]string{newoutfiles[0]}, gofiles...) - } + if len(infiles) != 0 { + // Coverage instrumentation creates new top level + // variables in the target package for things like + // meta-data containers, counter vars, etc. To avoid + // collisions with user variables, suffix the var name + // with 12 hex digits from the SHA-256 hash of the + // import path. Choice of 12 digits is historical/arbitrary, + // we just need enough of the hash to avoid accidents, + // as opposed to precluding determined attempts by + // users to break things. + sum := sha256.Sum256([]byte(a.Package.ImportPath)) + coverVar := fmt.Sprintf("goCover_%x_", sum[:6]) + mode := a.Package.Internal.Cover.Mode + if mode == "" { + panic("covermode should be set at this point") + } + if newoutfiles, err := b.cover(a, infiles, outfiles, coverVar, mode); err != nil { + return err } else { - // If there are no input files passed to cmd/cover, - // then we don't want to pass -covercfg when building - // the package with the compiler, so set covermode to - // the empty string so as to signal that we need to do - // that. - p.Internal.Cover.Mode = "" + outfiles = newoutfiles + gofiles = append([]string{newoutfiles[0]}, gofiles...) } if ba, ok := a.Actor.(*buildActor); ok && ba.covMetaFileName != "" { b.cacheObjdirFile(a, cache.Default(), ba.covMetaFileName) @@ -1374,6 +1358,7 @@ func (b *Builder) linkActionID(a *Action) cache.ActionID { fmt.Fprintf(h, "buildmode %s goos %s goarch %s\n", cfg.BuildBuildmode, cfg.Goos, cfg.Goarch) fmt.Fprintf(h, "import %q\n", p.ImportPath) fmt.Fprintf(h, "omitdebug %v standard %v local %v prefix %q\n", p.Internal.OmitDebug, p.Standard, p.Internal.Local, p.Internal.LocalPrefix) + fmt.Fprintf(h, "defaultgodebug %q\n", p.DefaultGODEBUG) if cfg.BuildTrimpath { fmt.Fprintln(h, "trimpath") } @@ -1908,26 +1893,13 @@ func (b *Builder) installHeader(ctx context.Context, a *Action) error { // cover runs, in effect, // -// go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go -func (b *Builder) cover(a *Action, dst, src string, varName string) error { - return b.Shell(a).run(a.Objdir, "", nil, - cfg.BuildToolexec, - base.Tool("cover"), - "-mode", a.Package.Internal.Cover.Mode, - "-var", varName, - "-o", dst, - src) -} - -// cover2 runs, in effect, -// // go tool cover -pkgcfg= -mode=b.coverMode -var="varName" -o // // Return value is an updated output files list; in addition to the // regular outputs (instrumented source files) the cover tool also // writes a separate file (appearing first in the list of outputs) // that will contain coverage counters and meta-data. -func (b *Builder) cover2(a *Action, infiles, outfiles []string, varName string, mode string) ([]string, error) { +func (b *Builder) cover(a *Action, infiles, outfiles []string, varName string, mode string) ([]string, error) { pkgcfg := a.Objdir + "pkgcfg.txt" covoutputs := a.Objdir + "coveroutfiles.txt" odir := filepath.Dir(outfiles[0]) diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go index 1e2f81b2d4d23e..c3d62ddc23d83a 100644 --- a/src/cmd/go/internal/work/security.go +++ b/src/cmd/go/internal/work/security.go @@ -96,17 +96,21 @@ var validCompilerFlags = []*lazyregexp.Regexp{ re(`-g([^@\-].*)?`), re(`-m32`), re(`-m64`), - re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), + re(`-m(abi|arch|cpu|fpu|simd|tls-dialect|tune)=([^@\-].*)`), re(`-m(no-)?v?aes`), re(`-marm`), re(`-m(no-)?avx[0-9a-z]*`), re(`-mcmodel=[0-9a-z-]+`), re(`-mfloat-abi=([^@\-].*)`), + re(`-m(soft|single|double)-float`), re(`-mfpmath=[0-9a-z,+]*`), re(`-m(no-)?avx[0-9a-z.]*`), re(`-m(no-)?ms-bitfields`), re(`-m(no-)?stack-(.+)`), re(`-mmacosx-(.+)`), + re(`-m(no-)?relax`), + re(`-m(no-)?strict-align`), + re(`-m(no-)?(lsx|lasx|frecipe|div32|lam-bh|lamcas|ld-seq-sa)`), re(`-mios-simulator-version-min=(.+)`), re(`-miphoneos-version-min=(.+)`), re(`-mlarge-data-threshold=[0-9]+`), @@ -166,8 +170,13 @@ var validLinkerFlags = []*lazyregexp.Regexp{ re(`-flat_namespace`), re(`-g([^@\-].*)?`), re(`-headerpad_max_install_names`), - re(`-m(abi|arch|cpu|fpu|tune)=([^@\-].*)`), + re(`-m(abi|arch|cpu|fpu|simd|tls-dialect|tune)=([^@\-].*)`), + re(`-mcmodel=[0-9a-z-]+`), re(`-mfloat-abi=([^@\-].*)`), + re(`-m(soft|single|double)-float`), + re(`-m(no-)?relax`), + re(`-m(no-)?strict-align`), + re(`-m(no-)?(lsx|lasx|frecipe|div32|lam-bh|lamcas|ld-seq-sa)`), re(`-mmacosx-(.+)`), re(`-mios-simulator-version-min=(.+)`), re(`-miphoneos-version-min=(.+)`), @@ -201,23 +210,23 @@ var validLinkerFlags = []*lazyregexp.Regexp{ re(`-Wl,--end-group`), re(`-Wl,--(no-)?export-dynamic`), re(`-Wl,-E`), - re(`-Wl,-framework,[^,@\-][^,]+`), + re(`-Wl,-framework,[^,@\-][^,]*`), re(`-Wl,--hash-style=(sysv|gnu|both)`), re(`-Wl,-headerpad_max_install_names`), re(`-Wl,--no-undefined`), re(`-Wl,--pop-state`), re(`-Wl,--push-state`), re(`-Wl,-R,?([^@\-,][^,@]*$)`), - re(`-Wl,--just-symbols[=,]([^,@\-][^,@]+)`), - re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]+)`), + re(`-Wl,--just-symbols[=,]([^,@\-][^,@]*)`), + re(`-Wl,-rpath(-link)?[=,]([^,@\-][^,]*)`), re(`-Wl,-s`), re(`-Wl,-search_paths_first`), - re(`-Wl,-sectcreate,([^,@\-][^,]+),([^,@\-][^,]+),([^,@\-][^,]+)`), + re(`-Wl,-sectcreate,([^,@\-][^,]*),([^,@\-][^,]*),([^,@\-][^,]*)`), re(`-Wl,--start-group`), re(`-Wl,-?-static`), re(`-Wl,-?-subsystem,(native|windows|console|posix|xbox)`), - re(`-Wl,-syslibroot[=,]([^,@\-][^,]+)`), - re(`-Wl,-undefined[=,]([^,@\-][^,]+)`), + re(`-Wl,-syslibroot[=,]([^,@\-][^,]*)`), + re(`-Wl,-undefined[=,]([^,@\-][^,]*)`), re(`-Wl,-?-unresolved-symbols=[^,]+`), re(`-Wl,--(no-)?warn-([^,]+)`), re(`-Wl,-?-wrap[=,][^,@\-][^,]*`), diff --git a/src/cmd/go/internal/work/security_test.go b/src/cmd/go/internal/work/security_test.go index 63dd569f7d3964..48f98100a51bd1 100644 --- a/src/cmd/go/internal/work/security_test.go +++ b/src/cmd/go/internal/work/security_test.go @@ -50,10 +50,35 @@ var goodCompilerFlags = [][]string{ {"-ftls-model=local-dynamic"}, {"-g"}, {"-ggdb"}, + {"-mabi=lp64d"}, {"-march=souza"}, {"-mcmodel=medium"}, {"-mcpu=123"}, {"-mfpu=123"}, + {"-mtls-dialect=gnu"}, + {"-mtls-dialect=gnu2"}, + {"-mtls-dialect=trad"}, + {"-mtls-dialect=desc"}, + {"-mtls-dialect=xyz"}, + {"-msimd=lasx"}, + {"-msimd=xyz"}, + {"-mdouble-float"}, + {"-mrelax"}, + {"-mstrict-align"}, + {"-mlsx"}, + {"-mlasx"}, + {"-mfrecipe"}, + {"-mlam-bh"}, + {"-mlamcas"}, + {"-mld-seq-sa"}, + {"-mno-relax"}, + {"-mno-strict-align"}, + {"-mno-lsx"}, + {"-mno-lasx"}, + {"-mno-frecipe"}, + {"-mno-lam-bh"}, + {"-mno-lamcas"}, + {"-mno-ld-seq-sa"}, {"-mlarge-data-threshold=16"}, {"-mtune=happybirthday"}, {"-mstack-overflow"}, @@ -96,7 +121,13 @@ var badCompilerFlags = [][]string{ {"-march=@dawn"}, {"-march=-dawn"}, {"-mcmodel=@model"}, + {"-mfpu=@0"}, + {"-mfpu=-0"}, {"-mlarge-data-threshold=@12"}, + {"-mtls-dialect=@gnu"}, + {"-mtls-dialect=-gnu"}, + {"-msimd=@none"}, + {"-msimd=-none"}, {"-std=@c99"}, {"-std=-c99"}, {"-x@c"}, @@ -182,6 +213,13 @@ var goodLinkerFlags = [][]string{ {"-Wl,--pop-state"}, {"-Wl,--push-state,--as-needed"}, {"-Wl,--push-state,--no-as-needed,-Bstatic"}, + {"-Wl,--just-symbols,."}, + {"-Wl,-framework,."}, + {"-Wl,-rpath,."}, + {"-Wl,-rpath-link,."}, + {"-Wl,-sectcreate,.,.,."}, + {"-Wl,-syslibroot,."}, + {"-Wl,-undefined,."}, } var badLinkerFlags = [][]string{ diff --git a/src/cmd/go/scriptconds_test.go b/src/cmd/go/scriptconds_test.go index 262214f6a96c76..af9691ad2a4aca 100644 --- a/src/cmd/go/scriptconds_test.go +++ b/src/cmd/go/scriptconds_test.go @@ -37,6 +37,7 @@ func scriptConditions(t *testing.T) map[string]script.Cond { } add("abscc", script.Condition("default $CC path is absolute and exists", defaultCCIsAbsolute)) + add("bzr", lazyBool("the 'bzr' executable exists and provides the standard CLI", hasWorkingBzr)) add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive)) add("cc", script.PrefixCondition("go env CC = (ignoring the go/env file)", ccIs)) add("git", lazyBool("the 'git' executable exists and provides the standard CLI", hasWorkingGit)) @@ -151,3 +152,14 @@ func hasWorkingGit() bool { _, err := exec.LookPath("git") return err == nil } + +func hasWorkingBzr() bool { + bzr, err := exec.LookPath("bzr") + if err != nil { + return false + } + // Check that 'bzr help' exits with code 0. + // See go.dev/issue/71504 for an example where 'bzr' exists in PATH but doesn't work. + err = exec.Command(bzr, "help").Run() + return err == nil +} diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README index 8c95945ebe0761..7724bc10ec46f0 100644 --- a/src/cmd/go/testdata/script/README +++ b/src/cmd/go/testdata/script/README @@ -377,6 +377,8 @@ The available conditions are: GOOS/GOARCH supports -asan [buildmode:*] go supports -buildmode= +[bzr] + the 'bzr' executable exists and provides the standard CLI [case-sensitive] $WORK filesystem is case-sensitive [cc:*] diff --git a/src/cmd/go/testdata/script/build_cacheprog_issue70848.txt b/src/cmd/go/testdata/script/build_cacheprog_issue70848.txt new file mode 100644 index 00000000000000..194fd47d93443d --- /dev/null +++ b/src/cmd/go/testdata/script/build_cacheprog_issue70848.txt @@ -0,0 +1,27 @@ +[short] skip 'builds go programs' + +go build -o cacheprog$GOEXE cacheprog.go +env GOCACHEPROG=$GOPATH/src/cacheprog$GOEXE + +# This should not deadlock +go build simple.go +! stderr 'cacheprog closed' + +-- simple.go -- +package main + +func main() {} +-- cacheprog.go -- +// This is a minimal GOCACHEPROG program that doesn't respond to close. +package main + +import ( + "encoding/json" + "os" +) + +func main() { + json.NewEncoder(os.Stdout).Encode(map[string][]string{"KnownCommands": {"close"}}) + var res struct{} + json.NewDecoder(os.Stdin).Decode(&res) +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/build_cc_cache_issue64589.txt b/src/cmd/go/testdata/script/build_cc_cache_issue64589.txt new file mode 100644 index 00000000000000..42ae91b196c2b6 --- /dev/null +++ b/src/cmd/go/testdata/script/build_cc_cache_issue64589.txt @@ -0,0 +1,100 @@ +# Regression test for https://go.dev/issue/64589: +# This test is very similar to build_cc_cache_issue64423. Issue #64423 +# was that we weren't properly parsing the versions output by the compiler. +# That test checked that we could parse the version and incorporate the +# version into the hash for the action id. This issue #64589 is that +# we treat all errors getting the version of the compiler the same, so +# we'd get the same action id for a missing compiler vs one whose +# version is unparseable. So the test now first does a run with a compiler +# that produces unparseable version output, and then runs it again with a missing +# compiler and ensures the command doesn't return the cached output for the +# first run when running the second run. + +[!cgo] skip +[short] skip 'builds and links a fake clang binary' +[!cc:clang] skip 'test is specific to clang version parsing' + +# Save the location of the real clang command for our fake one to use. +go run ./which clang +cp stdout $WORK/.realclang + +# Build a fake clang and ensure that it is the one in $PATH. +mkdir $WORK/bin +go build -o $WORK/bin/clang$GOEXE ./fakeclang +[!GOOS:plan9] env PATH=$WORK${/}bin +[GOOS:plan9] env path=$WORK${/}bin + +# Force CGO_ENABLED=1 so that the following commands should error +# out if the fake clang doesn't work. +env CGO_ENABLED=1 + +# The bug in https://go.dev/issue/64589 resulted in cache keys that +# didn't contain any information about the error getting the compiler version. +# Since the bug was in cache key computation, isolate the cache: +# if we change the way caching works, we want the test to fail +# instead of accidentally reusing the cached information from a +# previous test run. +env GOCACHE=$WORK${/}.cache +mkdir $GOCACHE + +go build -x runtime/cgo + + # Tell our fake clang to stop working. + # Previously, 'go build -x runtime/cgo' would continue to + # succeed because both the broken clang and the non-broken one + # resulted in a cache key with no clang version information. +env GO_BREAK_CLANG=1 +! go build -x runtime/cgo +stderr '# runtime/cgo\nGO_BREAK_CLANG is set' + +-- go.mod -- +module example/issue64589 +go 1.20 +-- which/main.go -- +package main + +import ( + "os" + "os/exec" +) + +func main() { + path, err := exec.LookPath(os.Args[1]) + if err != nil { + panic(err) + } + os.Stdout.WriteString(path) +} +-- fakeclang/main.go -- +package main + +import ( + "bytes" + "log" + "os" + "os/exec" + "path/filepath" + "slices" +) + +func main() { + if os.Getenv("GO_BREAK_CLANG") != "" { + os.Stderr.WriteString("GO_BREAK_CLANG is set\n") + os.Exit(1) + } + + b, err := os.ReadFile(filepath.Join(os.Getenv("WORK"), ".realclang")) + if err != nil { + log.Fatal(err) + } + if slices.Contains(os.Args, "-###") { // We are being run by gccToolID to determine the tool id used in the action id. + return // The important thing is that we don't print the string "version"! + } + clang := string(bytes.TrimSpace(b)) + cmd := exec.Command(clang, os.Args[1:]...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + log.Fatal(err) + } +} diff --git a/src/cmd/go/testdata/script/build_version_stamping_git.txt b/src/cmd/go/testdata/script/build_version_stamping_git.txt index ed07e00c7b192a..db804b38479960 100644 --- a/src/cmd/go/testdata/script/build_version_stamping_git.txt +++ b/src/cmd/go/testdata/script/build_version_stamping_git.txt @@ -51,7 +51,7 @@ go version -m example$GOEXE stdout '\s+mod\s+example\s+v1.0.1\s+' rm example$GOEXE -# Use tag+dirty when there are uncomitted changes present. +# Use tag+dirty when there are uncommitted changes present. cp $WORK/copy/README $WORK/repo/README go build go version -m example$GOEXE @@ -82,7 +82,7 @@ go version -m example$GOEXE stdout '\s+mod\s+example\s+v1.0.3-0.20220719150702-deaeab06f7fe\s+' rm example$GOEXE -# Use pseudo+dirty when uncomitted changes are present. +# Use pseudo+dirty when uncommitted changes are present. mv README2 README3 go build go version -m example$GOEXE diff --git a/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt b/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt index e14a0784f2bc9d..ba382639e9cdf2 100644 --- a/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt +++ b/src/cmd/go/testdata/script/cover_build_cmdline_pkgs.txt @@ -5,7 +5,6 @@ # inside and outside the standard library. [short] skip -[!GOEXPERIMENT:coverageredesign] skip # Compile an object. go tool compile -p tiny tiny/tiny.go tiny/tiny2.go diff --git a/src/cmd/go/testdata/script/cover_build_pkg_select.txt b/src/cmd/go/testdata/script/cover_build_pkg_select.txt index 447ca7788c13ea..3a59e72e88317a 100644 --- a/src/cmd/go/testdata/script/cover_build_pkg_select.txt +++ b/src/cmd/go/testdata/script/cover_build_pkg_select.txt @@ -3,9 +3,6 @@ [short] skip -# Skip if new coverage is not enabled. -[!GOEXPERIMENT:coverageredesign] skip - #------------------------------------------- # Build for coverage. diff --git a/src/cmd/go/testdata/script/cover_build_simple.txt b/src/cmd/go/testdata/script/cover_build_simple.txt index b61e631abd9339..ee451a68ab18bf 100644 --- a/src/cmd/go/testdata/script/cover_build_simple.txt +++ b/src/cmd/go/testdata/script/cover_build_simple.txt @@ -2,9 +2,6 @@ [short] skip -# Hard-wire new coverage for this test. -env GOEXPERIMENT=coverageredesign - # Build for coverage. go build -gcflags=-m -o example.exe -cover example/main & [race] go build -o examplewithrace.exe -race -cover example/main & diff --git a/src/cmd/go/testdata/script/cover_coverpkg_partial.txt b/src/cmd/go/testdata/script/cover_coverpkg_partial.txt index ef7a4dd2aac48f..efdfe2f27d85bc 100644 --- a/src/cmd/go/testdata/script/cover_coverpkg_partial.txt +++ b/src/cmd/go/testdata/script/cover_coverpkg_partial.txt @@ -16,7 +16,6 @@ # [short] skip -[!GOEXPERIMENT:coverageredesign] skip # Test all packages with -coverpkg=./... go test -coverprofile=cov.p -coverpkg=./... ./... diff --git a/src/cmd/go/testdata/script/cover_coverpkg_with_init.txt b/src/cmd/go/testdata/script/cover_coverpkg_with_init.txt index 7a89102547adb2..bb5bcfa823979f 100644 --- a/src/cmd/go/testdata/script/cover_coverpkg_with_init.txt +++ b/src/cmd/go/testdata/script/cover_coverpkg_with_init.txt @@ -5,7 +5,6 @@ # do not, some have tests and some do not. [short] skip -[!GOEXPERIMENT:coverageredesign] skip # Verify correct statements percentages. We have a total of 10 # statements in the packages matched by "./..."; package "a" (for diff --git a/src/cmd/go/testdata/script/cover_coverprofile_nocoverpkg.txt b/src/cmd/go/testdata/script/cover_coverprofile_nocoverpkg.txt new file mode 100644 index 00000000000000..f077734045c9c7 --- /dev/null +++ b/src/cmd/go/testdata/script/cover_coverprofile_nocoverpkg.txt @@ -0,0 +1,44 @@ +# Testcase for #70244. In this bug we're doing a "go test -coverprofile" +# run for a pair of packages, the first one without tests and the second +# one with tests. When writing the profile for the second test, profile +# data from the first package was leaking into the output (we should +# only see lines in the output profile for the package whose test is +# being run). + +[short] skip + +# Kick off test. +go test -vet=off -count=1 -coverprofile=cov.p ./... + +# Generate a function profile. +go tool cover -func=cov.p + +stdout 'cov/pkg1/file.go:3:\s+DoSomething\s+0.0%' + +-- go.mod -- +module cov + +-- pkg1/file.go -- +package pkg1 + +func DoSomething() bool { + return true +} +-- pkg2/file.go -- +package pkg2 + +func DoSomething() bool { + return true +} +-- pkg2/file_test.go -- +package pkg2 + +import ( + "cov/pkg1" + "testing" +) + +func TestSmth(t *testing.T) { + pkg1.DoSomething() + DoSomething() +} diff --git a/src/cmd/go/testdata/script/cover_list.txt b/src/cmd/go/testdata/script/cover_list.txt index 1b1f3266622155..7ecc5c05ebfb85 100644 --- a/src/cmd/go/testdata/script/cover_list.txt +++ b/src/cmd/go/testdata/script/cover_list.txt @@ -3,7 +3,6 @@ # build arguments (such as -cover, -covermode). See issue #57785. [short] skip -[!GOEXPERIMENT:coverageredesign] skip env GOBIN=$WORK/bin diff --git a/src/cmd/go/testdata/script/cover_main_import_path.txt b/src/cmd/go/testdata/script/cover_main_import_path.txt index e8696e27e23bae..3a2f3c3ee28fee 100644 --- a/src/cmd/go/testdata/script/cover_main_import_path.txt +++ b/src/cmd/go/testdata/script/cover_main_import_path.txt @@ -4,7 +4,6 @@ # the "main" package is handled. See issue 57169 for details. [short] skip -[!GOEXPERIMENT:coverageredesign] skip # Build this program with -cover and run to collect a profile. diff --git a/src/cmd/go/testdata/script/cover_statements.txt b/src/cmd/go/testdata/script/cover_statements.txt index 030177cb8b4acf..f0391ede22d064 100644 --- a/src/cmd/go/testdata/script/cover_statements.txt +++ b/src/cmd/go/testdata/script/cover_statements.txt @@ -10,35 +10,32 @@ env GOCACHE=$WORK/cache # Initial run with simple coverage. go test -cover ./pkg1 ./pkg2 ./pkg3 ./pkg4 -[!GOEXPERIMENT:coverageredesign] stdout 'pkg1 \[no test files\]' -[GOEXPERIMENT:coverageredesign] stdout 'pkg1 coverage: 0.0% of statements' +stdout 'pkg1 coverage: 0.0% of statements' stdout 'pkg2 \S+ coverage: 0.0% of statements \[no tests to run\]' stdout 'pkg3 \S+ coverage: 100.0% of statements' stdout 'pkg4 \S+ coverage: \[no statements\]' # Second run to make sure that caching works properly. go test -x -cover ./pkg1 ./pkg2 ./pkg3 ./pkg4 -[!GOEXPERIMENT:coverageredesign] stdout 'pkg1 \[no test files\]' -[GOEXPERIMENT:coverageredesign] stdout 'pkg1 coverage: 0.0% of statements' +stdout 'pkg1 coverage: 0.0% of statements' stdout 'pkg2 \S+ coverage: 0.0% of statements \[no tests to run\]' stdout 'pkg3 \S+ coverage: 100.0% of statements' stdout 'pkg4 \S+ coverage: \[no statements\]' -[GOEXPERIMENT:coverageredesign] ! stderr 'link(\.exe"?)? -' +! stderr 'link(\.exe"?)? -' ! stderr 'compile(\.exe"?)? -' ! stderr 'cover(\.exe"?)? -' -[GOEXPERIMENT:coverageredesign] stderr 'covdata(\.exe"?)? percent' +stderr 'covdata(\.exe"?)? percent' # Now add in -coverprofile. go test -cover -coverprofile=cov.dat ./pkg1 ./pkg2 ./pkg3 ./pkg4 -[!GOEXPERIMENT:coverageredesign] stdout 'pkg1 \[no test files\]' -[GOEXPERIMENT:coverageredesign] stdout 'pkg1 coverage: 0.0% of statements' +stdout 'pkg1 coverage: 0.0% of statements' stdout 'pkg2 \S+ coverage: 0.0% of statements \[no tests to run\]' stdout 'pkg3 \S+ coverage: 100.0% of statements' stdout 'pkg4 \S+ coverage: \[no statements\]' # Validate go tool cover -func=cov.dat -[GOEXPERIMENT:coverageredesign] stdout 'pkg1/a.go:5:\s+F\s+0.0%' +stdout 'pkg1/a.go:5:\s+F\s+0.0%' -- go.mod -- module m diff --git a/src/cmd/go/testdata/script/cover_sync_atomic_import.txt b/src/cmd/go/testdata/script/cover_sync_atomic_import.txt index a098979797acf3..7beea137e2d637 100644 --- a/src/cmd/go/testdata/script/cover_sync_atomic_import.txt +++ b/src/cmd/go/testdata/script/cover_sync_atomic_import.txt @@ -1,6 +1,5 @@ [short] skip [compiler:gccgo] skip # gccgo has no cover tool -[!GOEXPERIMENT:coverageredesign] skip go test -short -cover -covermode=atomic -coverpkg=coverdep/p1 coverdep diff --git a/src/cmd/go/testdata/script/cover_test_pkgselect.txt b/src/cmd/go/testdata/script/cover_test_pkgselect.txt index 97a1d2cbbb0c85..8e47d142d41ccb 100644 --- a/src/cmd/go/testdata/script/cover_test_pkgselect.txt +++ b/src/cmd/go/testdata/script/cover_test_pkgselect.txt @@ -1,9 +1,6 @@ [short] skip -# Hard-wire new coverage for this test. -env GOEXPERIMENT=coverageredesign - # Baseline run. go test -cover example/foo stdout 'coverage: 50.0% of statements$' diff --git a/src/cmd/go/testdata/script/cover_var_init_order.txt b/src/cmd/go/testdata/script/cover_var_init_order.txt index 37e07b71f657d5..a9ee63c78c891e 100644 --- a/src/cmd/go/testdata/script/cover_var_init_order.txt +++ b/src/cmd/go/testdata/script/cover_var_init_order.txt @@ -4,9 +4,6 @@ [short] skip -# Skip if new coverage is turned off. -[!GOEXPERIMENT:coverageredesign] skip - go test -cover example -- go.mod -- diff --git a/src/cmd/go/testdata/script/env_changed.txt b/src/cmd/go/testdata/script/env_changed.txt index f57f69bfd78ca0..10db7654070615 100644 --- a/src/cmd/go/testdata/script/env_changed.txt +++ b/src/cmd/go/testdata/script/env_changed.txt @@ -1,5 +1,8 @@ # Test query for non-defaults in the env +# Go+BoringCrypto conflicts with GOFIPS140. +[GOEXPERIMENT:boringcrypto] skip + env GOROOT=./a env GOTOOLCHAIN=local env GOSUMDB=nodefault diff --git a/src/cmd/go/testdata/script/env_gocacheprog.txt b/src/cmd/go/testdata/script/env_gocacheprog.txt new file mode 100644 index 00000000000000..1547bf058c16ca --- /dev/null +++ b/src/cmd/go/testdata/script/env_gocacheprog.txt @@ -0,0 +1,42 @@ +# GOCACHEPROG unset +env GOCACHEPROG= + +go env +stdout 'GOCACHEPROG=''?''?' + +go env -changed +! stdout 'GOCACHEPROG' + +go env -changed -json +! stdout 'GOCACHEPROG' + +# GOCACHEPROG set +[short] skip 'compiles and runs a go program' + +go build -o cacheprog$GOEXE cacheprog.go + +env GOCACHEPROG=$GOPATH/src/cacheprog$GOEXE + +go env +stdout 'GOCACHEPROG=''?'$GOCACHEPROG'''?' + +go env -changed +stdout 'GOCACHEPROG=''?'$GOCACHEPROG'''?' + +go env -changed -json +stdout '"GOCACHEPROG": ".*cacheprog'$GOEXE'"' + +-- cacheprog.go -- +// This is a minimal GOCACHEPROG program that can't actually do anything but exit. +package main + +import ( + "encoding/json" + "os" +) + +func main() { + json.NewEncoder(os.Stdout).Encode(map[string][]string{"KnownCommands": {"close"}}) + var res struct{} + json.NewDecoder(os.Stdin).Decode(&res) +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/fips.txt b/src/cmd/go/testdata/script/fips.txt index fd791d39903e88..374902eb702a2e 100644 --- a/src/cmd/go/testdata/script/fips.txt +++ b/src/cmd/go/testdata/script/fips.txt @@ -1,3 +1,6 @@ +# Go+BoringCrypto conflicts with GOFIPS140. +[GOEXPERIMENT:boringcrypto] skip + # list with GOFIPS140=off env GOFIPS140=off go list -f '{{.DefaultGODEBUG}}' @@ -17,12 +20,12 @@ go build -x -o x.exe go build -x -o x.exe ! stderr link -# build with GOFIPS140=latest is NOT cached (need fipso) +# build with GOFIPS140=latest is cached too env GOFIPS140=latest go build -x -o x.exe stderr link.*-fipso go build -x -o x.exe -stderr link.*-fipso +! stderr link.*-fipso # build test with GOFIPS140=off is cached env GOFIPS140=off @@ -38,8 +41,6 @@ stderr link.*-fipso go test -x -c ! stderr link - - -- go.mod -- module m -- x.go -- diff --git a/src/cmd/go/testdata/script/fipssnap.txt b/src/cmd/go/testdata/script/fipssnap.txt index 17a9d647a1845b..9888bc82f11d81 100644 --- a/src/cmd/go/testdata/script/fipssnap.txt +++ b/src/cmd/go/testdata/script/fipssnap.txt @@ -1,12 +1,11 @@ -## Note: Need a snapshot in lib/fips140 to run this test. -## For local testing, can run 'cd lib/fips140; make v0.0.1.test' -## and then remove the skip. -env snap=v0.0.1 +env snap=v1.0.0 env alias=inprocess -skip 'no snapshots yet' env GOFIPS140=$snap +# Go+BoringCrypto conflicts with GOFIPS140. +[GOEXPERIMENT:boringcrypto] skip + # default GODEBUG includes fips140=on go list -f '{{.DefaultGODEBUG}}' stdout fips140=on @@ -24,7 +23,8 @@ stdout crypto/internal/fips140/$snap/sha256 ! stdout crypto/internal/fips140/check # again with GOFIPS140=$alias -env GOFIPS140=$alias +# TODO: enable when we add inprocess.txt +# env GOFIPS140=$alias # default GODEBUG includes fips140=on go list -f '{{.DefaultGODEBUG}}' @@ -44,11 +44,11 @@ stdout crypto/internal/fips140/$snap/sha256 [short] skip -# build with GOFIPS140=snap is NOT cached (need fipso) +# build with GOFIPS140=snap is cached go build -x -o x.exe stderr link.*-fipso go build -x -o x.exe -stderr link.*-fipso +! stderr link.*-fipso # build test with GOFIPS140=snap is cached go test -x -c diff --git a/src/cmd/go/testdata/script/goauth_git.txt b/src/cmd/go/testdata/script/goauth_git.txt index 4fae39aaa71040..37c9b19a045806 100644 --- a/src/cmd/go/testdata/script/goauth_git.txt +++ b/src/cmd/go/testdata/script/goauth_git.txt @@ -49,6 +49,8 @@ go get vcs-test.golang.org/auth/or401 go mod tidy go list all stdout vcs-test.golang.org/auth/or404 +# With cached credentials, re-downloading in debug mode should succeed. +go get -x vcs-test.golang.org/auth/or401 # Clearing GOAUTH credentials should result in failures. env GOAUTH='off' diff --git a/src/cmd/go/testdata/script/goauth_netrc.txt b/src/cmd/go/testdata/script/goauth_netrc.txt index 2dda119e825d9f..0baa09de1ecaf9 100644 --- a/src/cmd/go/testdata/script/goauth_netrc.txt +++ b/src/cmd/go/testdata/script/goauth_netrc.txt @@ -2,8 +2,6 @@ # credentials passed in HTTPS requests to VCS servers. # See golang.org/issue/26232 -[short] skip - env GOPROXY=direct env GOSUMDB=off @@ -56,6 +54,18 @@ env NETRC=$WORK/missing ! go get vcs-test.golang.org/auth/or401 stderr '^\tserver response: ACCESS DENIED, buddy$' +[short] skip 'requires a remote vcs lookup' +[!git] skip +# An unset home directory should warn the user but not cause a failure. +env NETRC= +env HOME= +env USERPROFILE= +env home= +go get -x vcs-test.golang.org/git/emptytest.git +[!GOOS:windows] [!GOOS:plan9] stderr 'GOAUTH=netrc: \$HOME is not defined' +[GOOS:windows] stderr 'GOAUTH=netrc: \%userprofile\% is not defined' +[GOOS:plan9] stderr 'GOAUTH=netrc: \$home is not defined' + -- go.mod -- module private.example.com -- $WORK/empty -- @@ -63,3 +73,7 @@ module private.example.com machine vcs-test.golang.org login aladdin password opensesame +# first one should override this one +machine vcs-test.golang.org + login aladdin + password ignored diff --git a/src/cmd/go/testdata/script/goauth_userauth.txt b/src/cmd/go/testdata/script/goauth_userauth.txt index 8403c37125d050..036573e07a2a3f 100644 --- a/src/cmd/go/testdata/script/goauth_userauth.txt +++ b/src/cmd/go/testdata/script/goauth_userauth.txt @@ -3,13 +3,8 @@ env GOPROXY=direct env GOSUMDB=off - -# Use a custom authenticator to provide custom credentials mkdir $WORK/bin env PATH=$WORK/bin${:}$PATH -cd auth -go build -o $WORK/bin/my-auth$GOEXE . -cd .. # Without credentials, downloading a module from a path that requires HTTPS # basic auth should fail. @@ -21,8 +16,21 @@ stderr '^\tserver response: ACCESS DENIED, buddy$' ! go mod tidy stderr '^\tserver response: ACCESS DENIED, buddy$' -# With credentials from the my-auth binary, it should succeed. -env GOAUTH='my-auth'$GOEXE' --arg1 "value with spaces"' +# Initial invocation of authenticator is successful. +go build -o $WORK/bin/basic$GOEXE scripts/basic.go +# With credentials from the binary, it should succeed. +env GOAUTH='basic'$GOEXE +cp go.mod.orig go.mod +go get vcs-test.golang.org/auth/or401 +# go imports should resolve correctly as well. +go mod tidy +go list all +stdout vcs-test.golang.org/auth/or401 + +# Second invocation of authenticator is successful. +go build -o $WORK/bin/reinvocation$GOEXE scripts/reinvocation.go +# With credentials from the binary, it should succeed. +env GOAUTH='reinvocation'$GOEXE cp go.mod.orig go.mod go get vcs-test.golang.org/auth/or401 # go imports should resolve correctly as well. @@ -30,7 +38,43 @@ go mod tidy go list all stdout vcs-test.golang.org/auth/or401 --- auth/main.go -- +# Authenticator can parse arguments correctly. +go build -o $WORK/bin/arguments$GOEXE scripts/arguments.go +# With credentials from the binary, it should succeed. +env GOAUTH='arguments'$GOEXE' --arg1 "value with spaces"' +cp go.mod.orig go.mod +go get vcs-test.golang.org/auth/or401 +# go imports should resolve correctly as well. +go mod tidy +go list all +stdout vcs-test.golang.org/auth/or401 + +# Authenticator provides bad credentials. +go build -o $WORK/bin/invalid$GOEXE scripts/invalid.go +# With credentials from the binary, it should fail. +env GOAUTH='invalid'$GOEXE +cp go.mod.orig go.mod +! go get vcs-test.golang.org/auth/or401 +stderr '^\tserver response: ACCESS DENIED, buddy$' +# go imports should fail as well. +! go mod tidy +stderr '^\tserver response: ACCESS DENIED, buddy$' + +-- go.mod.orig -- +module private.example.com +-- main.go -- +package useprivate + +import "vcs-test.golang.org/auth/or401" +-- scripts/basic.go -- +package main + +import "fmt" + +func main() { + fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l\n\n") +} +-- scripts/reinvocation.go -- package main import( @@ -45,11 +89,7 @@ import( ) func main() { - arg1 := flag.String("arg1", "", "") flag.Parse() - if *arg1 != "value with spaces" { - log.Fatal("argument with spaces does not work") - } // wait for re-invocation if !strings.HasPrefix(flag.Arg(0), "https://vcs-test.golang.org") { return @@ -68,12 +108,28 @@ func main() { } fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l\n\n") } +-- scripts/arguments.go -- +package main --- auth/go.mod -- -module my-auth --- go.mod.orig -- -module private.example.com --- main.go -- -package useprivate +import( + "flag" + "fmt" + "log" +) -import "vcs-test.golang.org/auth/or401" +func main() { + arg1 := flag.String("arg1", "", "") + flag.Parse() + if *arg1 != "value with spaces" { + log.Fatal("argument with spaces does not work") + } + fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l\n\n") +} +-- scripts/invalid.go -- +package main + +import "fmt" + +func main() { + fmt.Printf("https://vcs-test.golang.org\n\nAuthorization: Basic invalid\n\n") +} \ No newline at end of file diff --git a/src/cmd/go/testdata/script/gotoolchain_local.txt b/src/cmd/go/testdata/script/gotoolchain_local.txt index db7e082db96749..8bece6ebd8439b 100644 --- a/src/cmd/go/testdata/script/gotoolchain_local.txt +++ b/src/cmd/go/testdata/script/gotoolchain_local.txt @@ -197,6 +197,17 @@ go mod edit -go=1.501 -toolchain=none go version stdout go1.501 +# avoid two-step switch, first from install target requirement, then from GOTOOLCHAIN min +# instead, just jump directly to GOTOOLCHAIN min +env TESTGO_VERSION=go1.2.3 +env GODEBUG=toolchaintrace=1 +env GOTOOLCHAIN=go1.23.0+auto +! go install rsc.io/fortune/nonexist@v0.0.1 +! stderr 'switching to go1.22.9' +stderr 'using go1.23.0' +env GODEBUG= +env GOTOOLCHAIN=auto + # go install m@v and go run m@v should ignore go.mod and use m@v env TESTGO_VERSION=go1.2.3 go mod edit -go=1.999 -toolchain=go1.998 diff --git a/src/cmd/go/testdata/script/mod_help.txt b/src/cmd/go/testdata/script/mod_help.txt index b5cd30c5219e88..7cb808ff237500 100644 --- a/src/cmd/go/testdata/script/mod_help.txt +++ b/src/cmd/go/testdata/script/mod_help.txt @@ -3,4 +3,4 @@ env GO111MODULE=on # go help get shows usage for get go help get stdout 'usage: go get' -stdout 'get using modules to manage source' \ No newline at end of file +stdout 'updates go.mod to require those versions' diff --git a/src/cmd/go/testdata/script/mod_unknown_block.txt b/src/cmd/go/testdata/script/mod_unknown_block.txt new file mode 100644 index 00000000000000..071269bb8db0ea --- /dev/null +++ b/src/cmd/go/testdata/script/mod_unknown_block.txt @@ -0,0 +1,11 @@ +env GOTOOLCHAIN=local +! go list . +stderr 'go: go.mod requires go >= 1.999' + + +-- go.mod -- +module example.com + +go 1.999 + +anewblock foo diff --git a/src/cmd/go/testdata/script/test_cache_inputs.txt b/src/cmd/go/testdata/script/test_cache_inputs.txt index 68a700b1160763..796a5880eb6146 100644 --- a/src/cmd/go/testdata/script/test_cache_inputs.txt +++ b/src/cmd/go/testdata/script/test_cache_inputs.txt @@ -128,6 +128,11 @@ go test testcache -run=TestOSArgs -fullpath go test testcache -run=TestOSArgs -fullpath stdout '\(cached\)' +# golang.org/issue/70692: that includes the `-skip` flag +go test testcache -run=TestOdd -skip=TestOddFile +! stdout '\(cached\)' +go test testcache -run=TestOdd -skip=TestOddFile +stdout '\(cached\)' # Executables within GOROOT and GOPATH should affect caching, # even if the test does not stat them explicitly. diff --git a/src/cmd/go/testdata/script/test_cleanup_failnow.txt b/src/cmd/go/testdata/script/test_cleanup_failnow.txt index 0aba8c7c00f8b9..80182cd9e3fae9 100644 --- a/src/cmd/go/testdata/script/test_cleanup_failnow.txt +++ b/src/cmd/go/testdata/script/test_cleanup_failnow.txt @@ -14,8 +14,8 @@ env GOGC=off ! go test -v cleanup_failnow/panic_nocleanup_test.go ! stdout 'no tests to run' -stdout '(?s)panic: die \[recovered\].*panic: die' -! stdout '(?s)panic: die \[recovered\].*panic: die.*panic: die' +stdout '(?s)panic: die \[recovered, reraised\]' +! stdout '(?s)panic: die \[recovered, reraised\].*panic: die' ! go test -v cleanup_failnow/panic_withcleanup_test.go ! stdout 'no tests to run' @@ -43,4 +43,4 @@ func TestCleanupWithFailNow(t *testing.T) { panic("die") }) }) -} \ No newline at end of file +} diff --git a/src/cmd/go/testdata/script/test_flags.txt b/src/cmd/go/testdata/script/test_flags.txt index 7adf4e273c569a..afef08840df87a 100644 --- a/src/cmd/go/testdata/script/test_flags.txt +++ b/src/cmd/go/testdata/script/test_flags.txt @@ -15,8 +15,8 @@ stdout '\Aok\s+example.com/x\s+[0-9.s]+\n\z' # Even though ./x looks like a package path, the real package should be # the implicit '.'. ! go test --answer=42 ./x -stdout '^FAIL\t. \[build failed\]' -stderr '^\.: no Go files in '$PWD'$' +stdout '^FAIL\t. \[setup failed\]' +stderr '^# \.\nno Go files in '$PWD'$' # However, *flags* that appear after unrecognized flags should still be # interpreted as flags, under the (possibly-erroneous) assumption that diff --git a/src/cmd/go/testdata/script/test_fuzz_context.txt b/src/cmd/go/testdata/script/test_fuzz_context.txt new file mode 100644 index 00000000000000..a830684708e65b --- /dev/null +++ b/src/cmd/go/testdata/script/test_fuzz_context.txt @@ -0,0 +1,47 @@ +[!fuzz] skip +[short] skip +env GOCACHE=$WORK/cache + +# Test fuzz.Context. +go test -vet=off context_fuzz_test.go +stdout ^ok +! stdout FAIL + +go test -vet=off -fuzz=Fuzz -fuzztime=1x context_fuzz_test.go +stdout ok +! stdout FAIL + +-- context_fuzz_test.go -- +package context_fuzz + +import ( + "context" + "errors" + "testing" +) + +func Fuzz(f *testing.F) { + ctx := f.Context() + if err := ctx.Err(); err != nil { + f.Fatalf("expected non-canceled context, got %v", err) + } + + f.Fuzz(func(t *testing.T, data []byte) { + innerCtx := t.Context() + if err := innerCtx.Err(); err != nil { + t.Fatalf("expected inner test to not inherit canceled context, got %v", err) + } + + t.Cleanup(func() { + if !errors.Is(innerCtx.Err(), context.Canceled) { + t.Fatal("expected context of inner test to be canceled after its fuzz function finished") + } + }) + }) + + f.Cleanup(func() { + if !errors.Is(ctx.Err(), context.Canceled) { + f.Fatal("expected context canceled before cleanup") + } + }) +} diff --git a/src/cmd/go/testdata/script/test_fuzz_return.txt b/src/cmd/go/testdata/script/test_fuzz_return.txt index 2f7b85bcc011b0..d86783e9cb9bb1 100644 --- a/src/cmd/go/testdata/script/test_fuzz_return.txt +++ b/src/cmd/go/testdata/script/test_fuzz_return.txt @@ -3,7 +3,7 @@ # Disable vet, as its "tests" analyzer would report the same problem statically. ! go test -vet=off . -stdout '^panic: testing: fuzz target must not return a value \[recovered\]$' +stdout '^panic: testing: fuzz target must not return a value \[recovered, reraised\]$' -- go.mod -- module test diff --git a/src/cmd/go/testdata/script/test_json_build.txt b/src/cmd/go/testdata/script/test_json_build.txt index a3f0c37923faf5..df8863ae03287f 100644 --- a/src/cmd/go/testdata/script/test_json_build.txt +++ b/src/cmd/go/testdata/script/test_json_build.txt @@ -40,6 +40,18 @@ stdout '"Action":"output","Package":"m/loaderror","Output":"FAIL\\tm/loaderror \ stdout '"Action":"fail","Package":"m/loaderror","Elapsed":.*,"FailedBuild":"x"' ! stderr '.' +# Test an import cycle loading error in a non test file. (#70820) +! go test -json -o=$devnull ./cycle/p +stdout '"ImportPath":"m/cycle/q","Action":"build-output","Output":"# m/cycle/p\\n"' +stdout '"ImportPath":"m/cycle/q","Action":"build-output","Output":"package m/cycle/p\\n"' +stdout '"ImportPath":"m/cycle/q","Action":"build-output","Output":"\\timports m/cycle/q from p.go\\n"' +stdout '"ImportPath":"m/cycle/q","Action":"build-output","Output":"\\timports m/cycle/q from q.go: import cycle not allowed\\n"' +stdout '"ImportPath":"m/cycle/q","Action":"build-fail"' +stdout '"Action":"start","Package":"m/cycle/p"' +stdout '"Action":"output","Package":"m/cycle/p","Output":"FAIL\\tm/cycle/p \[setup failed\]\\n"' +stdout '"Action":"fail","Package":"m/cycle/p","Elapsed":.*,"FailedBuild":"m/cycle/q"' +! stderr '.' + # Test a vet error ! go test -json -o=$devnull ./veterror stdout '"ImportPath":"m/veterror \[m/veterror.test\]","Action":"build-output","Output":"# m/veterror\\n"' @@ -58,8 +70,9 @@ stderr '# m/builderror \[m/builderror.test\]\n' stderr 'builderror'${/}'main_test.go:3:11: undefined: y\n' stdout '"Action":"start","Package":"m/builderror"' stdout '"Action":"output","Package":"m/builderror","Output":"FAIL\\tm/builderror \[build failed\]\\n"' -stdout '"Action":"fail","Package":"m/builderror","Elapsed":.*,"FailedBuild":"m/builderror \[m/builderror\.test\]"' - +stdout '"Action":"fail","Package":"m/builderror","Elapsed":[0-9.]+\}' +# FailedBuild should NOT appear in the output in this mode. +! stdout '"FailedBuild"' -- go.mod -- module m @@ -98,3 +111,13 @@ import ( func TestVetError(t *testing.T) { fmt.Printf("%s") } +-- cycle/p/p.go -- +package p + +import "m/cycle/q" +-- cycle/q/q.go -- +package q + +import ( + "m/cycle/q" +) diff --git a/src/cmd/go/testdata/script/test_setup_error.txt b/src/cmd/go/testdata/script/test_setup_error.txt index 2999067f2c93f3..bf566d4621f1a5 100644 --- a/src/cmd/go/testdata/script/test_setup_error.txt +++ b/src/cmd/go/testdata/script/test_setup_error.txt @@ -33,10 +33,23 @@ stderr '# m/t2/p\n.*package x is not in std' stdout 'FAIL m/t2/p \[setup failed\]' stdout 'ok m/t' -# Finally, this one is a build error, but produced by cmd/go directly +# Test that an import cycle error is reported. Test for #70820 +! go test -o=$devnull ./cycle/p ./t +stderr '# m/cycle/p\n.*package m/cycle/p\n\timports m/cycle/p from p\.go: import cycle not allowed' +stdout 'FAIL m/cycle/p \[setup failed\]' +stdout 'ok m/t' + +# Test that multiple errors for the same package under test are reported. +! go test -o=$devnull ./cycle/q ./t +stderr '# m/cycle/q\n.*package m/cycle/q\n\timports m/cycle/p from q\.go\n\timports m/cycle/p from p\.go: import cycle not allowed' +stdout 'FAIL m/cycle/q \[setup failed\]' +stdout 'ok m/t' + +# Finally, this one is a non-import-cycle load error that +# is produced for the package under test. ! go test -o=$devnull . ./t -stderr '^\.: no Go files in '$PWD'$' -stdout 'FAIL . \[build failed\]' +stderr '# \.\n.*no Go files in '$PWD'$' +stdout 'FAIL . \[setup failed\]' stdout 'ok m/t' -- go.mod -- @@ -68,6 +81,17 @@ package p package p import "m/bad" +-- cycle/p/p.go -- +package p + +import "m/cycle/p" +-- cycle/q/q.go -- +package q + +import ( + "m/bad" + "m/cycle/p" +) -- bad/bad.go -- package bad diff --git a/src/cmd/go/testdata/script/testing_coverage.txt b/src/cmd/go/testdata/script/testing_coverage.txt index 6cf6adbbd7a49f..bf4dc83107b3b9 100644 --- a/src/cmd/go/testdata/script/testing_coverage.txt +++ b/src/cmd/go/testdata/script/testing_coverage.txt @@ -2,7 +2,6 @@ # Rudimentary test of testing.Coverage(). [short] skip -[!GOEXPERIMENT:coverageredesign] skip # Simple test. go test -v -cover -count=1 diff --git a/src/cmd/go/testdata/script/tooltags.txt b/src/cmd/go/testdata/script/tooltags.txt index 1f6f54563ca97a..a69b7a5c37b746 100644 --- a/src/cmd/go/testdata/script/tooltags.txt +++ b/src/cmd/go/testdata/script/tooltags.txt @@ -50,10 +50,15 @@ env GORISCV64=rva22u64 go list -f '{{context.ToolTags}}' stdout 'riscv64.rva20u64 riscv64.rva22u64' +env GOARCH=riscv64 +env GORISCV64=rva23u64 +go list -f '{{context.ToolTags}}' +stdout 'riscv64.rva20u64 riscv64.rva22u64 riscv64.rva23u64' + env GOARCH=riscv64 env GORISCV64=rva22 ! go list -f '{{context.ToolTags}}' -stderr 'go: invalid GORISCV64: must be rva20u64, rva22u64' +stderr 'go: invalid GORISCV64: must be rva20u64, rva22u64, rva23u64' env GOARCH=riscv64 env GORISCV64= diff --git a/src/cmd/go/testdata/script/version_buildvcs_bzr.txt b/src/cmd/go/testdata/script/version_buildvcs_bzr.txt index fc80f45677799e..59796d1ffa8c2d 100644 --- a/src/cmd/go/testdata/script/version_buildvcs_bzr.txt +++ b/src/cmd/go/testdata/script/version_buildvcs_bzr.txt @@ -2,8 +2,8 @@ # controlled with -buildvcs. This test focuses on Bazaar specifics. # The Git test covers common functionality. -[!exec:bzr] skip [short] skip +[!bzr] skip 'requires a working bzr client' env GOBIN=$WORK/gopath/bin env oldpath=$PATH env HOME=$WORK diff --git a/src/cmd/go/testdata/vcstest/bzr/hello.txt b/src/cmd/go/testdata/vcstest/bzr/hello.txt index 59315852f73652..68465ec553bd55 100644 --- a/src/cmd/go/testdata/vcstest/bzr/hello.txt +++ b/src/cmd/go/testdata/vcstest/bzr/hello.txt @@ -1,3 +1,4 @@ +[!bzr] skip 'requires a working bzr client' handle bzr env BZR_EMAIL='Russ Cox ' diff --git a/src/cmd/internal/bio/must.go b/src/cmd/internal/bio/must.go deleted file mode 100644 index 3604b291757479..00000000000000 --- a/src/cmd/internal/bio/must.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 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. - -package bio - -import ( - "io" - "log" -) - -// MustClose closes Closer c and calls log.Fatal if it returns a non-nil error. -func MustClose(c io.Closer) { - if err := c.Close(); err != nil { - log.Fatal(err) - } -} - -// MustWriter returns a Writer that wraps the provided Writer, -// except that it calls log.Fatal instead of returning a non-nil error. -func MustWriter(w io.Writer) io.Writer { - return mustWriter{w} -} - -type mustWriter struct { - w io.Writer -} - -func (w mustWriter) Write(b []byte) (int, error) { - n, err := w.w.Write(b) - if err != nil { - log.Fatal(err) - } - return n, nil -} - -func (w mustWriter) WriteString(s string) (int, error) { - n, err := io.WriteString(w.w, s) - if err != nil { - log.Fatal(err) - } - return n, nil -} diff --git a/src/cmd/internal/cov/read_test.go b/src/cmd/internal/cov/read_test.go index fa2151a09ed2ed..cef03fa323e6e8 100644 --- a/src/cmd/internal/cov/read_test.go +++ b/src/cmd/internal/cov/read_test.go @@ -11,7 +11,6 @@ import ( "internal/coverage/decodecounter" "internal/coverage/decodemeta" "internal/coverage/pods" - "internal/goexperiment" "internal/testenv" "os" "path/filepath" @@ -45,9 +44,6 @@ func (v *visitor) Finish() func TestIssue58411(t *testing.T) { testenv.MustHaveGoBuild(t) - if !goexperiment.CoverageRedesign { - t.Skipf("skipping since this test requires 'go build -cover'") - } // Build a tiny test program with -cover. Smallness is important; // it is one of the factors that triggers issue 58411. diff --git a/src/cmd/internal/objfile/disasm.go b/src/cmd/internal/disasm/disasm.go similarity index 94% rename from src/cmd/internal/objfile/disasm.go rename to src/cmd/internal/disasm/disasm.go index 99f54143fa9863..3ae8989b38d080 100644 --- a/src/cmd/internal/objfile/disasm.go +++ b/src/cmd/internal/disasm/disasm.go @@ -2,13 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package objfile +// Package disasm provides disassembly routines. +// +// It is broken out from cmd/internal/objfile so tools that don't need +// disassembling don't need to depend on x/arch disassembler code. +package disasm import ( "bufio" "bytes" "container/list" - "debug/gosym" "encoding/binary" "fmt" "io" @@ -19,6 +22,7 @@ import ( "strings" "text/tabwriter" + "cmd/internal/objfile" "cmd/internal/src" "golang.org/x/arch/arm/armasm" @@ -32,8 +36,8 @@ import ( // Disasm is a disassembler for a given File. type Disasm struct { - syms []Sym //symbols in file, sorted by address - pcln Liner // pcln table + syms []objfile.Sym // symbols in file, sorted by address + pcln objfile.Liner // pcln table text []byte // bytes of text segment (actual instructions) textStart uint64 // start PC of text textEnd uint64 // end PC of text @@ -42,8 +46,12 @@ type Disasm struct { byteOrder binary.ByteOrder // byte order for goarch } -// Disasm returns a disassembler for the file f. -func (e *Entry) Disasm() (*Disasm, error) { +// DisasmForFile returns a disassembler for the file f. +func DisasmForFile(f *objfile.File) (*Disasm, error) { + return disasmForEntry(f.Entries()[0]) +} + +func disasmForEntry(e *objfile.Entry) (*Disasm, error) { syms, err := e.Symbols() if err != nil { return nil, err @@ -269,7 +277,7 @@ func (d *Disasm) Print(w io.Writer, filter *regexp.Regexp, start, end uint64, pr } // Decode disassembles the text segment range [start, end), calling f for each instruction. -func (d *Disasm) Decode(start, end uint64, relocs []Reloc, gnuAsm bool, f func(pc, size uint64, file string, line int, text string)) { +func (d *Disasm) Decode(start, end uint64, relocs []objfile.Reloc, gnuAsm bool, f func(pc, size uint64, file string, line int, text string)) { if start < d.textStart { start = d.textStart } @@ -402,14 +410,16 @@ func disasm_ppc64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.By func disasm_riscv64(code []byte, pc uint64, lookup lookupFunc, byteOrder binary.ByteOrder, gnuAsm bool) (string, int) { inst, err := riscv64asm.Decode(code) var text string + size := inst.Len if err != nil || inst.Op == 0 { + size = 2 text = "?" } else if gnuAsm { text = fmt.Sprintf("%-36s // %s", riscv64asm.GoSyntax(inst, pc, lookup, textReader{code, pc}), riscv64asm.GNUSyntax(inst)) } else { text = riscv64asm.GoSyntax(inst, pc, lookup, textReader{code, pc}) } - return text, 4 + return text, size } func disasm_s390x(code []byte, pc uint64, lookup lookupFunc, _ binary.ByteOrder, gnuAsm bool) (string, int) { @@ -452,9 +462,3 @@ var byteOrders = map[string]binary.ByteOrder{ "riscv64": binary.LittleEndian, "s390x": binary.BigEndian, } - -type Liner interface { - // Given a pc, returns the corresponding file, line, and function data. - // If unknown, returns "",0,nil. - PCToLine(uint64) (string, int, *gosym.Func) -} diff --git a/src/cmd/internal/goobj/builtinlist.go b/src/cmd/internal/goobj/builtinlist.go index c133c60427f598..3e550d8dd9763b 100644 --- a/src/cmd/internal/goobj/builtinlist.go +++ b/src/cmd/internal/goobj/builtinlist.go @@ -110,11 +110,13 @@ var builtins = [...]struct { {"runtime.mapassign_fast64ptr", 1}, {"runtime.mapassign_faststr", 1}, {"runtime.mapiterinit", 1}, + {"runtime.mapIterStart", 1}, {"runtime.mapdelete", 1}, {"runtime.mapdelete_fast32", 1}, {"runtime.mapdelete_fast64", 1}, {"runtime.mapdelete_faststr", 1}, {"runtime.mapiternext", 1}, + {"runtime.mapIterNext", 1}, {"runtime.mapclear", 1}, {"runtime.makechan64", 1}, {"runtime.makechan", 1}, diff --git a/src/cmd/internal/hash/hash.go b/src/cmd/internal/hash/hash.go index 20edc72c20e648..cdb24a2645ccd5 100644 --- a/src/cmd/internal/hash/hash.go +++ b/src/cmd/internal/hash/hash.go @@ -6,51 +6,25 @@ package hash import ( - "crypto/md5" - "crypto/sha1" "crypto/sha256" "hash" ) -const ( - // Size32 is the size of 32 bytes hash checksum. - Size32 = sha256.Size - // Size20 is the size of 20 bytes hash checksum. - Size20 = sha1.Size - // Size16 is the size of 16 bytes hash checksum. - Size16 = md5.Size -) +// Size32 is the size of the 32-byte hash functions [New32] and [Sum32]. +const Size32 = 32 -// New32 returns a new [hash.Hash] computing the 32 bytes hash checksum. +// New32 returns a new [hash.Hash] computing the 32-byte hash checksum. +// Note that New32 and [Sum32] compute different hashes. func New32() hash.Hash { h := sha256.New() _, _ = h.Write([]byte{1}) // make this hash different from sha256 return h } -// New20 returns a new [hash.Hash] computing the 20 bytes hash checksum. -func New20() hash.Hash { - return sha1.New() -} - -// New16 returns a new [hash.Hash] computing the 16 bytes hash checksum. -func New16() hash.Hash { - return md5.New() -} - -// Sum32 returns the 32 bytes checksum of the data. -func Sum32(data []byte) [Size32]byte { +// Sum32 returns a 32-byte checksum of the data. +// Note that Sum32 and [New32] compute different hashes. +func Sum32(data []byte) [32]byte { sum := sha256.Sum256(data) - sum[0] ^= 1 // make this hash different from sha256 + sum[0] ^= 0xff // make this hash different from sha256 return sum } - -// Sum20 returns the 20 bytes checksum of the data. -func Sum20(data []byte) [Size20]byte { - return sha1.Sum(data) -} - -// Sum16 returns the 16 bytes checksum of the data. -func Sum16(data []byte) [Size16]byte { - return md5.Sum(data) -} diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index ad00e4842cdfc6..de04a242803df9 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -1055,8 +1055,8 @@ type SpecialOperand int const ( // PRFM - SPOP_PLDL1KEEP SpecialOperand = iota // must be the first one - SPOP_BEGIN SpecialOperand = iota - 1 // set as the lower bound + SPOP_PLDL1KEEP SpecialOperand = obj.SpecialOperandARM64Base + iota // must be the first one + SPOP_BEGIN SpecialOperand = obj.SpecialOperandARM64Base + iota - 1 // set as the lower bound SPOP_PLDL1STRM SPOP_PLDL2KEEP SPOP_PLDL2STRM diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index 1b2d344eaf8311..dbd66714d269fe 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -98,7 +98,8 @@ import ( // val = string // // -// Special symbolic constants for ARM64, such as conditional flags, tlbi_op and so on. +// Special symbolic constants for ARM64 (such as conditional flags, tlbi_op and so on) +// and RISCV64 (such as names for vector configuration instruction arguments). // Encoding: // type = TYPE_SPECIAL // offset = The constant value corresponding to this symbol @@ -603,6 +604,22 @@ func (s *LSym) NewTypeInfo() *TypeInfo { return t } +// An ItabInfo contains information for a symbol +// that contains a runtime.itab. +type ItabInfo struct { + Type interface{} // a *cmd/compile/internal/types.Type +} + +func (s *LSym) NewItabInfo() *ItabInfo { + if s.Extra != nil { + panic(fmt.Sprintf("invalid use of LSym - NewItabInfo with Extra of type %T", *s.Extra)) + } + t := new(ItabInfo) + s.Extra = new(interface{}) + *s.Extra = t + return t +} + // WasmImport represents a WebAssembly (WASM) imported function with // parameters and results translated into WASM types based on the Go function // declaration. diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index bc22765abcd91a..3299fbf4e6e1b0 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -494,7 +494,7 @@ func contentHash64(s *LSym) goobj.Hash64Type { // For now, we assume there is no circular dependencies among // hashed symbols. func (w *writer) contentHash(s *LSym) goobj.HashType { - h := hash.New20() + h := hash.New32() var tmp [14]byte // Include the size of the symbol in the hash. diff --git a/src/cmd/internal/obj/riscv/cpu.go b/src/cmd/internal/obj/riscv/cpu.go index 1501a5027ec99b..143164ac411139 100644 --- a/src/cmd/internal/obj/riscv/cpu.go +++ b/src/cmd/internal/obj/riscv/cpu.go @@ -317,6 +317,11 @@ const ( // it is the first instruction in an AUIPC + S-type pair that needs a // R_RISCV_PCREL_STYPE relocation. NEED_PCREL_STYPE_RELOC + + // NEED_GOT_PCREL_ITYPE_RELOC is set on AUIPC instructions to indicate that + // it is the first instruction in an AUIPC + I-type pair that needs a + // R_RISCV_GOT_PCREL_ITYPE relocation. + NEED_GOT_PCREL_ITYPE_RELOC ) // RISC-V mnemonics, as defined in the "opcodes" and "opcodes-pseudo" files @@ -571,6 +576,10 @@ const ( // 22.5 Quad-Precision Floating-Point Classify Instruction AFCLASSQ + // + // "B" Extension for Bit Manipulation, Version 1.0.0 + // + // 28.4.1: Address Generation Instructions (Zba) AADDUW ASH1ADD @@ -620,15 +629,15 @@ const ( ABSETI // - // RISC-V Vector ISA-extension (1.0) (Unprivileged 20240411) + // "V" Standard Extension for Vector Operations, Version 1.0 // - // 31.6. Configuration-Setting Instructions + // 31.6: Configuration-Setting Instructions AVSETVLI AVSETIVLI AVSETVL - // 31.7.4. Vector Unit-Stride Instructions + // 31.7.4: Vector Unit-Stride Instructions AVLE8V AVLE16V AVLE32V @@ -640,7 +649,7 @@ const ( AVLMV AVSMV - // 31.7.5. Vector Strided Instructions + // 31.7.5: Vector Strided Instructions AVLSE8V AVLSE16V AVLSE32V @@ -650,7 +659,7 @@ const ( AVSSE32V AVSSE64V - // 31.7.6. Vector Indexed Instructions + // 31.7.6: Vector Indexed Instructions AVLUXEI8V AVLUXEI16V AVLUXEI32V @@ -668,13 +677,13 @@ const ( AVSOXEI32V AVSOXEI64V - // 31.7.7. Unit-stride Fault-Only-First Loads + // 31.7.7: Unit-stride Fault-Only-First Loads AVLE8FFV AVLE16FFV AVLE32FFV AVLE64FFV - // 31.7.9. Vector Load/Store Whole Register Instructions + // 31.7.9: Vector Load/Store Whole Register Instructions AVL1RE8V AVL1RE16V AVL1RE32V @@ -696,7 +705,7 @@ const ( AVS4RV AVS8RV - // 31.11.1. Vector Single-Width Integer Add and Subtract + // 31.11.1: Vector Single-Width Integer Add and Subtract AVADDVV AVADDVX AVADDVI @@ -705,7 +714,7 @@ const ( AVRSUBVX AVRSUBVI - // 31.11.2. Vector Widening Integer Add/Subtract + // 31.11.2: Vector Widening Integer Add/Subtract AVWADDUVV AVWADDUVX AVWSUBUVV @@ -723,7 +732,7 @@ const ( AVWSUBWV AVWSUBWX - // 31.11.3. Vector Integer Extension + // 31.11.3: Vector Integer Extension AVZEXTVF2 AVSEXTVF2 AVZEXTVF4 @@ -731,7 +740,7 @@ const ( AVZEXTVF8 AVSEXTVF8 - // 31.11.4. Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions + // 31.11.4: Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions AVADCVVM AVADCVXM AVADCVIM @@ -748,7 +757,7 @@ const ( AVMSBCVV AVMSBCVX - // 31.11.5. Vector Bitwise Logical Instructions + // 31.11.5: Vector Bitwise Logical Instructions AVANDVV AVANDVX AVANDVI @@ -759,7 +768,7 @@ const ( AVXORVX AVXORVI - // 31.11.6. Vector Single-Width Shift Instructions + // 31.11.6: Vector Single-Width Shift Instructions AVSLLVV AVSLLVX AVSLLVI @@ -770,7 +779,7 @@ const ( AVSRAVX AVSRAVI - // 31.11.7. Vector Narrowing Integer Right Shift Instructions + // 31.11.7: Vector Narrowing Integer Right Shift Instructions AVNSRLWV AVNSRLWX AVNSRLWI @@ -778,7 +787,7 @@ const ( AVNSRAWX AVNSRAWI - // 31.11.8. Vector Integer Compare Instructions + // 31.11.8: Vector Integer Compare Instructions AVMSEQVV AVMSEQVX AVMSEQVI @@ -800,7 +809,7 @@ const ( AVMSGTVX AVMSGTVI - // 31.11.9. Vector Integer Min/Max Instructions + // 31.11.9: Vector Integer Min/Max Instructions AVMINUVV AVMINUVX AVMINVV @@ -810,7 +819,7 @@ const ( AVMAXVV AVMAXVX - // 31.11.10. Vector Single-Width Integer Multiply Instructions + // 31.11.10: Vector Single-Width Integer Multiply Instructions AVMULVV AVMULVX AVMULHVV @@ -820,7 +829,7 @@ const ( AVMULHSUVV AVMULHSUVX - // 31.11.11. Vector Integer Divide Instructions + // 31.11.11: Vector Integer Divide Instructions AVDIVUVV AVDIVUVX AVDIVVV @@ -830,7 +839,7 @@ const ( AVREMVV AVREMVX - // 31.11.12. Vector Widening Integer Multiply Instructions + // 31.11.12: Vector Widening Integer Multiply Instructions AVWMULVV AVWMULVX AVWMULUVV @@ -838,7 +847,7 @@ const ( AVWMULSUVV AVWMULSUVX - // 31.11.13. Vector Single-Width Integer Multiply-Add Instructions + // 31.11.13: Vector Single-Width Integer Multiply-Add Instructions AVMACCVV AVMACCVX AVNMSACVV @@ -848,7 +857,7 @@ const ( AVNMSUBVV AVNMSUBVX - // 31.11.14. Vector Widening Integer Multiply-Add Instructions + // 31.11.14: Vector Widening Integer Multiply-Add Instructions AVWMACCUVV AVWMACCUVX AVWMACCVV @@ -857,17 +866,17 @@ const ( AVWMACCSUVX AVWMACCUSVX - // 31.11.15. Vector Integer Merge Instructions + // 31.11.15: Vector Integer Merge Instructions AVMERGEVVM AVMERGEVXM AVMERGEVIM - // 31.11.16. Vector Integer Move Instructions + // 31.11.16: Vector Integer Move Instructions AVMVVV AVMVVX AVMVVI - // 31.12.1. Vector Single-Width Saturating Add and Subtract + // 31.12.1: Vector Single-Width Saturating Add and Subtract AVSADDUVV AVSADDUVX AVSADDUVI @@ -879,7 +888,7 @@ const ( AVSSUBVV AVSSUBVX - // 31.12.2. Vector Single-Width Averaging Add and Subtract + // 31.12.2: Vector Single-Width Averaging Add and Subtract AVAADDUVV AVAADDUVX AVAADDVV @@ -889,11 +898,11 @@ const ( AVASUBVV AVASUBVX - // 31.12.3. Vector Single-Width Fractional Multiply with Rounding and Saturation + // 31.12.3: Vector Single-Width Fractional Multiply with Rounding and Saturation AVSMULVV AVSMULVX - // 31.12.4. Vector Single-Width Scaling Shift Instructions + // 31.12.4: Vector Single-Width Scaling Shift Instructions AVSSRLVV AVSSRLVX AVSSRLVI @@ -901,7 +910,7 @@ const ( AVSSRAVX AVSSRAVI - // 31.12.5. Vector Narrowing Fixed-Point Clip Instructions + // 31.12.5: Vector Narrowing Fixed-Point Clip Instructions AVNCLIPUWV AVNCLIPUWX AVNCLIPUWI @@ -909,14 +918,14 @@ const ( AVNCLIPWX AVNCLIPWI - // 31.13.2. Vector Single-Width Floating-Point Add/Subtract Instructions + // 31.13.2: Vector Single-Width Floating-Point Add/Subtract Instructions AVFADDVV AVFADDVF AVFSUBVV AVFSUBVF AVFRSUBVF - // 31.13.3. Vector Widening Floating-Point Add/Subtract Instructions + // 31.13.3: Vector Widening Floating-Point Add/Subtract Instructions AVFWADDVV AVFWADDVF AVFWSUBVV @@ -926,18 +935,18 @@ const ( AVFWSUBWV AVFWSUBWF - // 31.13.4. Vector Single-Width Floating-Point Multiply/Divide Instructions + // 31.13.4: Vector Single-Width Floating-Point Multiply/Divide Instructions AVFMULVV AVFMULVF AVFDIVVV AVFDIVVF AVFRDIVVF - // 31.13.5. Vector Widening Floating-Point Multiply + // 31.13.5: Vector Widening Floating-Point Multiply AVFWMULVV AVFWMULVF - // 31.13.6. Vector Single-Width Floating-Point Fused Multiply-Add Instructions + // 31.13.6: Vector Single-Width Floating-Point Fused Multiply-Add Instructions AVFMACCVV AVFMACCVF AVFNMACCVV @@ -955,7 +964,7 @@ const ( AVFNMSUBVV AVFNMSUBVF - // 31.13.7. Vector Widening Floating-Point Fused Multiply-Add Instructions + // 31.13.7: Vector Widening Floating-Point Fused Multiply-Add Instructions AVFWMACCVV AVFWMACCVF AVFWNMACCVV @@ -965,22 +974,22 @@ const ( AVFWNMSACVV AVFWNMSACVF - // 31.13.8. Vector Floating-Point Square-Root Instruction + // 31.13.8: Vector Floating-Point Square-Root Instruction AVFSQRTV - // 31.13.9. Vector Floating-Point Reciprocal Square-Root Estimate Instruction + // 31.13.9: Vector Floating-Point Reciprocal Square-Root Estimate Instruction AVFRSQRT7V - // 31.13.10. Vector Floating-Point Reciprocal Estimate Instruction + // 31.13.10: Vector Floating-Point Reciprocal Estimate Instruction AVFREC7V - // 31.13.11. Vector Floating-Point MIN/MAX Instructions + // 31.13.11: Vector Floating-Point MIN/MAX Instructions AVFMINVV AVFMINVF AVFMAXVV AVFMAXVF - // 31.13.12. Vector Floating-Point Sign-Injection Instructions + // 31.13.12: Vector Floating-Point Sign-Injection Instructions AVFSGNJVV AVFSGNJVF AVFSGNJNVV @@ -988,7 +997,7 @@ const ( AVFSGNJXVV AVFSGNJXVF - // 31.13.13. Vector Floating-Point Compare Instructions + // 31.13.13: Vector Floating-Point Compare Instructions AVMFEQVV AVMFEQVF AVMFNEVV @@ -1000,16 +1009,16 @@ const ( AVMFGTVF AVMFGEVF - // 31.13.14. Vector Floating-Point Classify Instruction + // 31.13.14: Vector Floating-Point Classify Instruction AVFCLASSV - // 31.13.15. Vector Floating-Point Merge Instruction + // 31.13.15: Vector Floating-Point Merge Instruction AVFMERGEVFM - // 31.13.16. Vector Floating-Point Move Instruction + // 31.13.16: Vector Floating-Point Move Instruction AVFMVVF - // 31.13.17. Single-Width Floating-Point/Integer Type-Convert Instructions + // 31.13.17: Single-Width Floating-Point/Integer Type-Convert Instructions AVFCVTXUFV AVFCVTXFV AVFCVTRTZXUFV @@ -1017,7 +1026,7 @@ const ( AVFCVTFXUV AVFCVTFXV - // 31.13.18. Widening Floating-Point/Integer Type-Convert Instructions + // 31.13.18: Widening Floating-Point/Integer Type-Convert Instructions AVFWCVTXUFV AVFWCVTXFV AVFWCVTRTZXUFV @@ -1026,7 +1035,7 @@ const ( AVFWCVTFXV AVFWCVTFFV - // 31.13.19. Narrowing Floating-Point/Integer Type-Convert Instructions + // 31.13.19: Narrowing Floating-Point/Integer Type-Convert Instructions AVFNCVTXUFW AVFNCVTXFW AVFNCVTRTZXUFW @@ -1036,7 +1045,7 @@ const ( AVFNCVTFFW AVFNCVTRODFFW - // 31.14.1. Vector Single-Width Integer Reduction Instructions + // 31.14.1: Vector Single-Width Integer Reduction Instructions AVREDSUMVS AVREDMAXUVS AVREDMAXVS @@ -1046,21 +1055,21 @@ const ( AVREDORVS AVREDXORVS - // 31.14.2. Vector Widening Integer Reduction Instructions + // 31.14.2: Vector Widening Integer Reduction Instructions AVWREDSUMUVS AVWREDSUMVS - // 31.14.3. Vector Single-Width Floating-Point Reduction Instructions + // 31.14.3: Vector Single-Width Floating-Point Reduction Instructions AVFREDOSUMVS AVFREDUSUMVS AVFREDMAXVS AVFREDMINVS - // 31.14.4. Vector Widening Floating-Point Reduction Instructions + // 31.14.4: Vector Widening Floating-Point Reduction Instructions AVFWREDOSUMVS AVFWREDUSUMVS - // 31.15. Vector Mask Instructions + // 31.15: Vector Mask Instructions AVMANDMM AVMNANDMM AVMANDNMM @@ -1077,15 +1086,15 @@ const ( AVIOTAM AVIDV - // 31.16.1. Integer Scalar Move Instructions + // 31.16.1: Integer Scalar Move Instructions AVMVXS AVMVSX - // 31.16.2. Floating-Point Scalar Move Instructions + // 31.16.2: Floating-Point Scalar Move Instructions AVFMVFS AVFMVSF - // 31.16.3. Vector Slide Instructions + // 31.16.3: Vector Slide Instructions AVSLIDEUPVX AVSLIDEUPVI AVSLIDEDOWNVX @@ -1095,16 +1104,16 @@ const ( AVSLIDE1DOWNVX AVFSLIDE1DOWNVF - // 31.16.4. Vector Register Gather Instructions + // 31.16.4: Vector Register Gather Instructions AVRGATHERVV AVRGATHEREI16VV AVRGATHERVX AVRGATHERVI - // 31.16.5. Vector Compress Instruction + // 31.16.5: Vector Compress Instruction AVCOMPRESSVM - // 31.16.6. Whole Vector Register Move + // 31.16.6: Whole Vector Register Move AVMV1RV AVMV2RV AVMV4RV @@ -1218,6 +1227,77 @@ const ( RM_RMM // Round to Nearest, ties to Max Magnitude ) +type SpecialOperand int + +const ( + SPOP_BEGIN SpecialOperand = obj.SpecialOperandRISCVBase + + // Vector mask policy. + SPOP_MA SpecialOperand = obj.SpecialOperandRISCVBase + iota - 1 + SPOP_MU + + // Vector tail policy. + SPOP_TA + SPOP_TU + + // Vector register group multiplier (VLMUL). + SPOP_M1 + SPOP_M2 + SPOP_M4 + SPOP_M8 + SPOP_MF2 + SPOP_MF4 + SPOP_MF8 + + // Vector selected element width (VSEW). + SPOP_E8 + SPOP_E16 + SPOP_E32 + SPOP_E64 + + SPOP_END +) + +var specialOperands = map[SpecialOperand]struct { + encoding uint32 + name string +}{ + SPOP_MA: {encoding: 1, name: "MA"}, + SPOP_MU: {encoding: 0, name: "MU"}, + + SPOP_TA: {encoding: 1, name: "TA"}, + SPOP_TU: {encoding: 0, name: "TU"}, + + SPOP_M1: {encoding: 0, name: "M1"}, + SPOP_M2: {encoding: 1, name: "M2"}, + SPOP_M4: {encoding: 2, name: "M4"}, + SPOP_M8: {encoding: 3, name: "M8"}, + SPOP_MF2: {encoding: 5, name: "MF2"}, + SPOP_MF4: {encoding: 6, name: "MF4"}, + SPOP_MF8: {encoding: 7, name: "MF8"}, + + SPOP_E8: {encoding: 0, name: "E8"}, + SPOP_E16: {encoding: 1, name: "E16"}, + SPOP_E32: {encoding: 2, name: "E32"}, + SPOP_E64: {encoding: 3, name: "E64"}, +} + +func (so SpecialOperand) encode() uint32 { + op, ok := specialOperands[so] + if ok { + return op.encoding + } + return 0 +} + +func (so SpecialOperand) String() string { + op, ok := specialOperands[so] + if ok { + return op.name + } + return "" +} + // All unary instructions which write to their arguments (as opposed to reading // from them) go here. The assembly parser uses this information to populate // its AST in a semantically reasonable way. diff --git a/src/cmd/internal/obj/riscv/list.go b/src/cmd/internal/obj/riscv/list.go index c5b7e807199264..8eb97a476dc774 100644 --- a/src/cmd/internal/obj/riscv/list.go +++ b/src/cmd/internal/obj/riscv/list.go @@ -14,6 +14,7 @@ func init() { obj.RegisterRegister(obj.RBaseRISCV, REG_END, RegName) obj.RegisterOpcode(obj.ABaseRISCV, Anames) obj.RegisterOpSuffix("riscv64", opSuffixString) + obj.RegisterSpecialOperands(int64(SPOP_BEGIN), int64(SPOP_END), specialOperandConv) } func RegName(r int) string { @@ -49,3 +50,11 @@ func opSuffixString(s uint8) string { } return fmt.Sprintf(".%s", ss) } + +func specialOperandConv(a int64) string { + spc := SpecialOperand(a) + if spc >= SPOP_BEGIN && spc < SPOP_END { + return spc.String() + } + return "SPC_??" +} diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go index 5f01c43e7bf4c3..3a4ab556f7df2b 100644 --- a/src/cmd/internal/obj/riscv/obj.go +++ b/src/cmd/internal/obj/riscv/obj.go @@ -147,6 +147,15 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { p.From.Name = obj.NAME_EXTERN p.From.Offset = 0 } + + case AMOVD: + if p.From.Type == obj.TYPE_FCONST && p.From.Name == obj.NAME_NONE && p.From.Reg == obj.REG_NONE { + f64 := p.From.Val.(float64) + p.From.Type = obj.TYPE_MEM + p.From.Sym = ctxt.Float64Sym(f64) + p.From.Name = obj.NAME_EXTERN + p.From.Offset = 0 + } } } @@ -215,11 +224,15 @@ func markRelocs(p *obj.Prog) { switch p.From.Name { case obj.NAME_EXTERN, obj.NAME_STATIC: p.Mark |= NEED_PCREL_ITYPE_RELOC + case obj.NAME_GOTREF: + p.Mark |= NEED_GOT_PCREL_ITYPE_RELOC } case p.From.Type == obj.TYPE_MEM && p.To.Type == obj.TYPE_REG: switch p.From.Name { case obj.NAME_EXTERN, obj.NAME_STATIC: p.Mark |= NEED_PCREL_ITYPE_RELOC + case obj.NAME_GOTREF: + p.Mark |= NEED_GOT_PCREL_ITYPE_RELOC } case p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_MEM: switch p.To.Name { @@ -1038,27 +1051,35 @@ func immEven(x int64) error { return nil } -// immIFits checks whether the immediate value x fits in nbits bits -// as a signed integer. If it does not, an error is returned. -func immIFits(x int64, nbits uint) error { - nbits-- - min := int64(-1) << nbits - max := int64(1)< max { if nbits <= 16 { - return fmt.Errorf("signed immediate %d must be in range [%d, %d] (%d bits)", x, min, max, nbits) + return fmt.Errorf("%s immediate %d must be in range [%d, %d] (%d bits)", label, x, min, max, nbits) } - return fmt.Errorf("signed immediate %#x must be in range [%#x, %#x] (%d bits)", x, min, max, nbits) + return fmt.Errorf("%s immediate %#x must be in range [%#x, %#x] (%d bits)", label, x, min, max, nbits) } return nil } +// immIFits checks whether the immediate value x fits in nbits bits +// as a signed integer. If it does not, an error is returned. +func immIFits(x int64, nbits uint) error { + return immFits(x, nbits, true) +} + // immI extracts the signed integer of the specified size from an immediate. func immI(as obj.As, imm int64, nbits uint) uint32 { if err := immIFits(imm, nbits); err != nil { panic(fmt.Sprintf("%v: %v", as, err)) } - return uint32(imm) + return uint32(imm) & ((1 << nbits) - 1) } func wantImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) { @@ -1067,6 +1088,26 @@ func wantImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) { } } +// immUFits checks whether the immediate value x fits in nbits bits +// as an unsigned integer. If it does not, an error is returned. +func immUFits(x int64, nbits uint) error { + return immFits(x, nbits, false) +} + +// immU extracts the unsigned integer of the specified size from an immediate. +func immU(as obj.As, imm int64, nbits uint) uint32 { + if err := immUFits(imm, nbits); err != nil { + panic(fmt.Sprintf("%v: %v", as, err)) + } + return uint32(imm) & ((1 << nbits) - 1) +} + +func wantImmU(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) { + if err := immUFits(imm, nbits); err != nil { + ctxt.Diag("%v: %v", ins, err) + } +} + func wantReg(ctxt *obj.Link, ins *instruction, pos string, descr string, r, min, max uint32) { if r < min || r > max { var suffix string @@ -1223,6 +1264,29 @@ func validateJ(ctxt *obj.Link, ins *instruction) { wantNoneReg(ctxt, ins, "rs3", ins.rs3) } +func validateVsetvli(ctxt *obj.Link, ins *instruction) { + wantImmU(ctxt, ins, ins.imm, 11) + wantIntReg(ctxt, ins, "rd", ins.rd) + wantIntReg(ctxt, ins, "rs1", ins.rs1) + wantNoneReg(ctxt, ins, "rs2", ins.rs2) + wantNoneReg(ctxt, ins, "rs3", ins.rs3) +} + +func validateVsetivli(ctxt *obj.Link, ins *instruction) { + wantImmU(ctxt, ins, ins.imm, 10) + wantIntReg(ctxt, ins, "rd", ins.rd) + wantImmU(ctxt, ins, int64(ins.rs1), 5) + wantNoneReg(ctxt, ins, "rs2", ins.rs2) + wantNoneReg(ctxt, ins, "rs3", ins.rs3) +} + +func validateVsetvl(ctxt *obj.Link, ins *instruction) { + wantIntReg(ctxt, ins, "rd", ins.rd) + wantIntReg(ctxt, ins, "rs1", ins.rs1) + wantIntReg(ctxt, ins, "rs2", ins.rs2) + wantNoneReg(ctxt, ins, "rs3", ins.rs3) +} + func validateRaw(ctxt *obj.Link, ins *instruction) { // Treat the raw value specially as a 32-bit unsigned integer. // Nobody wants to enter negative machine code. @@ -1411,6 +1475,29 @@ func encodeCJImmediate(imm uint32) uint32 { return bits << 2 } +func encodeVset(as obj.As, rs1, rs2, rd uint32) uint32 { + enc := encode(as) + if enc == nil { + panic("encodeVset: could not encode instruction") + } + return enc.funct7<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | rd<<7 | enc.opcode +} + +func encodeVsetvli(ins *instruction) uint32 { + vtype := immU(ins.as, ins.imm, 11) + return encodeVset(ins.as, regI(ins.rs1), vtype, regI(ins.rd)) +} + +func encodeVsetivli(ins *instruction) uint32 { + vtype := immU(ins.as, ins.imm, 10) + avl := immU(ins.as, int64(ins.rs1), 5) + return encodeVset(ins.as, avl, vtype, regI(ins.rd)) +} + +func encodeVsetvl(ins *instruction) uint32 { + return encodeVset(ins.as, regI(ins.rs1), regI(ins.rs2), regI(ins.rd)) +} + func encodeRawIns(ins *instruction) uint32 { // Treat the raw value specially as a 32-bit unsigned integer. // Nobody wants to enter negative machine code. @@ -1481,6 +1568,27 @@ func EncodeUImmediate(imm int64) (int64, error) { return imm << 12, nil } +func EncodeVectorType(vsew, vlmul, vtail, vmask int64) (int64, error) { + vsewSO := SpecialOperand(vsew) + if vsewSO < SPOP_E8 || vsewSO > SPOP_E64 { + return -1, fmt.Errorf("invalid vector selected element width %q", vsewSO) + } + vlmulSO := SpecialOperand(vlmul) + if vlmulSO < SPOP_M1 || vlmulSO > SPOP_MF8 { + return -1, fmt.Errorf("invalid vector register group multiplier %q", vlmulSO) + } + vtailSO := SpecialOperand(vtail) + if vtailSO != SPOP_TA && vtailSO != SPOP_TU { + return -1, fmt.Errorf("invalid vector tail policy %q", vtailSO) + } + vmaskSO := SpecialOperand(vmask) + if vmaskSO != SPOP_MA && vmaskSO != SPOP_MU { + return -1, fmt.Errorf("invalid vector mask policy %q", vmaskSO) + } + vtype := vmaskSO.encode()<<7 | vtailSO.encode()<<6 | vsewSO.encode()<<3 | vlmulSO.encode() + return int64(vtype), nil +} + type encoding struct { encode func(*instruction) uint32 // encode returns the machine code for an instruction validate func(*obj.Link, *instruction) // validate validates an instruction @@ -1518,6 +1626,11 @@ var ( uEncoding = encoding{encode: encodeU, validate: validateU, length: 4} jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4} + // Encodings for vector configuration setting instruction. + vsetvliEncoding = encoding{encode: encodeVsetvli, validate: validateVsetvli, length: 4} + vsetivliEncoding = encoding{encode: encodeVsetivli, validate: validateVsetivli, length: 4} + vsetvlEncoding = encoding{encode: encodeVsetvl, validate: validateVsetvl, length: 4} + // rawEncoding encodes a raw instruction byte sequence. rawEncoding = encoding{encode: encodeRawIns, validate: validateRaw, length: 4} @@ -1540,7 +1653,9 @@ type instructionData struct { // their encoding type. Entries are masked with obj.AMask to keep // indices small. var instructions = [ALAST & obj.AMask]instructionData{ + // // Unprivileged ISA + // // 2.4: Integer Computational Instructions AADDI & obj.AMask: {enc: iIIEncoding, ternary: true}, @@ -1589,7 +1704,7 @@ var instructions = [ALAST & obj.AMask]instructionData{ // 2.7: Memory Ordering AFENCE & obj.AMask: {enc: iIIEncoding}, - // 5.2: Integer Computational Instructions (RV64I) + // 4.2: Integer Computational Instructions (RV64I) AADDIW & obj.AMask: {enc: iIIEncoding, ternary: true}, ASLLIW & obj.AMask: {enc: iIIEncoding, ternary: true}, ASRLIW & obj.AMask: {enc: iIIEncoding, ternary: true}, @@ -1600,14 +1715,14 @@ var instructions = [ALAST & obj.AMask]instructionData{ ASUBW & obj.AMask: {enc: rIIIEncoding, ternary: true}, ASRAW & obj.AMask: {enc: rIIIEncoding, immForm: ASRAIW, ternary: true}, - // 5.3: Load and Store Instructions (RV64I) + // 4.3: Load and Store Instructions (RV64I) ALD & obj.AMask: {enc: iIIEncoding}, ASD & obj.AMask: {enc: sIEncoding}, // 7.1: CSR Instructions ACSRRS & obj.AMask: {enc: iIIEncoding}, - // 7.1: Multiplication Operations + // 13.1: Multiplication Operations AMUL & obj.AMask: {enc: rIIIEncoding, ternary: true}, AMULH & obj.AMask: {enc: rIIIEncoding, ternary: true}, AMULHU & obj.AMask: {enc: rIIIEncoding, ternary: true}, @@ -1622,13 +1737,13 @@ var instructions = [ALAST & obj.AMask]instructionData{ AREMW & obj.AMask: {enc: rIIIEncoding, ternary: true}, AREMUW & obj.AMask: {enc: rIIIEncoding, ternary: true}, - // 8.2: Load-Reserved/Store-Conditional + // 14.2: Load-Reserved/Store-Conditional Instructions (Zalrsc) ALRW & obj.AMask: {enc: rIIIEncoding}, ALRD & obj.AMask: {enc: rIIIEncoding}, ASCW & obj.AMask: {enc: rIIIEncoding}, ASCD & obj.AMask: {enc: rIIIEncoding}, - // 8.3: Atomic Memory Operations + // 14.4: Atomic Memory Operations (Zaamo) AAMOSWAPW & obj.AMask: {enc: rIIIEncoding}, AAMOSWAPD & obj.AMask: {enc: rIIIEncoding}, AAMOADDW & obj.AMask: {enc: rIIIEncoding}, @@ -1648,11 +1763,11 @@ var instructions = [ALAST & obj.AMask]instructionData{ AAMOMINUW & obj.AMask: {enc: rIIIEncoding}, AAMOMINUD & obj.AMask: {enc: rIIIEncoding}, - // 11.5: Single-Precision Load and Store Instructions + // 20.5: Single-Precision Load and Store Instructions AFLW & obj.AMask: {enc: iFEncoding}, AFSW & obj.AMask: {enc: sFEncoding}, - // 11.6: Single-Precision Floating-Point Computational Instructions + // 20.6: Single-Precision Floating-Point Computational Instructions AFADDS & obj.AMask: {enc: rFFFEncoding}, AFSUBS & obj.AMask: {enc: rFFFEncoding}, AFMULS & obj.AMask: {enc: rFFFEncoding}, @@ -1665,7 +1780,7 @@ var instructions = [ALAST & obj.AMask]instructionData{ AFNMSUBS & obj.AMask: {enc: rFFFFEncoding}, AFNMADDS & obj.AMask: {enc: rFFFFEncoding}, - // 11.7: Single-Precision Floating-Point Conversion and Move Instructions + // 20.7: Single-Precision Floating-Point Conversion and Move Instructions AFCVTWS & obj.AMask: {enc: rFIEncoding}, AFCVTLS & obj.AMask: {enc: rFIEncoding}, AFCVTSW & obj.AMask: {enc: rIFEncoding}, @@ -1680,19 +1795,19 @@ var instructions = [ALAST & obj.AMask]instructionData{ AFMVXW & obj.AMask: {enc: rFIEncoding}, AFMVWX & obj.AMask: {enc: rIFEncoding}, - // 11.8: Single-Precision Floating-Point Compare Instructions + // 20.8: Single-Precision Floating-Point Compare Instructions AFEQS & obj.AMask: {enc: rFFIEncoding}, AFLTS & obj.AMask: {enc: rFFIEncoding}, AFLES & obj.AMask: {enc: rFFIEncoding}, - // 11.9: Single-Precision Floating-Point Classify Instruction + // 20.9: Single-Precision Floating-Point Classify Instruction AFCLASSS & obj.AMask: {enc: rFIEncoding}, // 12.3: Double-Precision Load and Store Instructions AFLD & obj.AMask: {enc: iFEncoding}, AFSD & obj.AMask: {enc: sFEncoding}, - // 12.4: Double-Precision Floating-Point Computational Instructions + // 21.4: Double-Precision Floating-Point Computational Instructions AFADDD & obj.AMask: {enc: rFFFEncoding}, AFSUBD & obj.AMask: {enc: rFFFEncoding}, AFMULD & obj.AMask: {enc: rFFFEncoding}, @@ -1705,7 +1820,7 @@ var instructions = [ALAST & obj.AMask]instructionData{ AFNMSUBD & obj.AMask: {enc: rFFFFEncoding}, AFNMADDD & obj.AMask: {enc: rFFFFEncoding}, - // 12.5: Double-Precision Floating-Point Conversion and Move Instructions + // 21.5: Double-Precision Floating-Point Conversion and Move Instructions AFCVTWD & obj.AMask: {enc: rFIEncoding}, AFCVTLD & obj.AMask: {enc: rFIEncoding}, AFCVTDW & obj.AMask: {enc: rIFEncoding}, @@ -1722,25 +1837,19 @@ var instructions = [ALAST & obj.AMask]instructionData{ AFMVXD & obj.AMask: {enc: rFIEncoding}, AFMVDX & obj.AMask: {enc: rIFEncoding}, - // 12.6: Double-Precision Floating-Point Compare Instructions + // 21.6: Double-Precision Floating-Point Compare Instructions AFEQD & obj.AMask: {enc: rFFIEncoding}, AFLTD & obj.AMask: {enc: rFFIEncoding}, AFLED & obj.AMask: {enc: rFFIEncoding}, - // 12.7: Double-Precision Floating-Point Classify Instruction + // 21.7: Double-Precision Floating-Point Classify Instruction AFCLASSD & obj.AMask: {enc: rFIEncoding}, - // Privileged ISA - - // 3.2.1: Environment Call and Breakpoint - AECALL & obj.AMask: {enc: iIIEncoding}, - AEBREAK & obj.AMask: {enc: iIIEncoding}, - // - // RISC-V Bit-Manipulation ISA-extensions (1.0) + // "B" Extension for Bit Manipulation, Version 1.0.0 // - // 1.1: Address Generation Instructions (Zba) + // 28.4.1: Address Generation Instructions (Zba) AADDUW & obj.AMask: {enc: rIIIEncoding, ternary: true}, ASH1ADD & obj.AMask: {enc: rIIIEncoding, ternary: true}, ASH1ADDUW & obj.AMask: {enc: rIIIEncoding, ternary: true}, @@ -1750,7 +1859,7 @@ var instructions = [ALAST & obj.AMask]instructionData{ ASH3ADDUW & obj.AMask: {enc: rIIIEncoding, ternary: true}, ASLLIUW & obj.AMask: {enc: iIIEncoding, ternary: true}, - // 1.2: Basic Bit Manipulation (Zbb) + // 28.4.2: Basic Bit Manipulation (Zbb) AANDN & obj.AMask: {enc: rIIIEncoding, ternary: true}, ACLZ & obj.AMask: {enc: rIIEncoding}, ACLZW & obj.AMask: {enc: rIIEncoding}, @@ -1768,7 +1877,7 @@ var instructions = [ALAST & obj.AMask]instructionData{ AXNOR & obj.AMask: {enc: rIIIEncoding, ternary: true}, AZEXTH & obj.AMask: {enc: rIIEncoding}, - // 1.3: Bitwise Rotation (Zbb) + // 28.4.3: Bitwise Rotation (Zbb) AROL & obj.AMask: {enc: rIIIEncoding, ternary: true}, AROLW & obj.AMask: {enc: rIIIEncoding, ternary: true}, AROR & obj.AMask: {enc: rIIIEncoding, immForm: ARORI, ternary: true}, @@ -1778,7 +1887,7 @@ var instructions = [ALAST & obj.AMask]instructionData{ AORCB & obj.AMask: {enc: iIIEncoding}, AREV8 & obj.AMask: {enc: iIIEncoding}, - // 1.5: Single-bit Instructions (Zbs) + // 28.4.4: Single-bit Instructions (Zbs) ABCLR & obj.AMask: {enc: rIIIEncoding, immForm: ABCLRI, ternary: true}, ABCLRI & obj.AMask: {enc: iIIEncoding, ternary: true}, ABEXT & obj.AMask: {enc: rIIIEncoding, immForm: ABEXTI, ternary: true}, @@ -1788,6 +1897,23 @@ var instructions = [ALAST & obj.AMask]instructionData{ ABSET & obj.AMask: {enc: rIIIEncoding, immForm: ABSETI, ternary: true}, ABSETI & obj.AMask: {enc: iIIEncoding, ternary: true}, + // + // "V" Standard Extension for Vector Operations, Version 1.0 + // + + // 31.6. Vector Configuration-Setting Instructions + AVSETVLI & obj.AMask: {enc: vsetvliEncoding, immForm: AVSETIVLI}, + AVSETIVLI & obj.AMask: {enc: vsetivliEncoding}, + AVSETVL & obj.AMask: {enc: vsetvlEncoding}, + + // + // Privileged ISA + // + + // 3.3.1: Environment Call and Breakpoint + AECALL & obj.AMask: {enc: iIIEncoding}, + AEBREAK & obj.AMask: {enc: iIIEncoding}, + // Escape hatch AWORD & obj.AMask: {enc: rawEncoding}, @@ -2199,18 +2325,25 @@ func instructionsForMOV(p *obj.Prog) []*instruction { // MOV c(Rs), Rd -> L $c, Rs, Rd inss = instructionsForLoad(p, movToLoad(p.As), addrToReg(p.From)) - case obj.NAME_EXTERN, obj.NAME_STATIC: + case obj.NAME_EXTERN, obj.NAME_STATIC, obj.NAME_GOTREF: if p.From.Sym.Type == objabi.STLSBSS { return instructionsForTLSLoad(p) } // Note that the values for $off_hi and $off_lo are currently - // zero and will be assigned during relocation. + // zero and will be assigned during relocation. If the destination + // is an integer register then we can use the same register for the + // address computation, otherwise we need to use the temporary register. // // AUIPC $off_hi, Rd // L $off_lo, Rd, Rd - insAUIPC := &instruction{as: AAUIPC, rd: ins.rd} - ins.as, ins.rs1, ins.rs2, ins.imm = movToLoad(p.As), ins.rd, obj.REG_NONE, 0 + // + addrReg := ins.rd + if addrReg < REG_X0 || addrReg > REG_X31 { + addrReg = REG_TMP + } + insAUIPC := &instruction{as: AAUIPC, rd: addrReg} + ins.as, ins.rs1, ins.rs2, ins.imm = movToLoad(p.As), addrReg, obj.REG_NONE, 0 inss = []*instruction{insAUIPC, ins} default: @@ -2337,7 +2470,12 @@ func instructionsForProg(p *obj.Prog) []*instruction { ins := instructionForProg(p) inss := []*instruction{ins} - if len(p.RestArgs) > 1 { + if ins.as == AVSETVLI || ins.as == AVSETIVLI { + if len(p.RestArgs) != 4 { + p.Ctxt.Diag("incorrect number of arguments for instruction") + return nil + } + } else if len(p.RestArgs) > 1 { p.Ctxt.Diag("too many source registers") return nil } @@ -2575,6 +2713,21 @@ func instructionsForProg(p *obj.Prog) []*instruction { // XNOR -> (NOT (XOR x y)) ins.as = AXOR inss = append(inss, &instruction{as: AXORI, rs1: ins.rd, rs2: obj.REG_NONE, rd: ins.rd, imm: -1}) + + case AVSETVLI, AVSETIVLI: + ins.rs1, ins.rs2 = ins.rs2, obj.REG_NONE + vtype, err := EncodeVectorType(p.RestArgs[0].Offset, p.RestArgs[1].Offset, p.RestArgs[2].Offset, p.RestArgs[3].Offset) + if err != nil { + p.Ctxt.Diag("%v: %v", p, err) + } + ins.imm = int64(vtype) + if ins.as == AVSETIVLI { + if p.From.Type != obj.TYPE_CONST { + p.Ctxt.Diag("%v: expected immediate value", p) + } + ins.rs1 = uint32(p.From.Offset) + } + } for _, ins := range inss { @@ -2627,6 +2780,9 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { } else if p.Mark&NEED_PCREL_STYPE_RELOC == NEED_PCREL_STYPE_RELOC { rt = objabi.R_RISCV_PCREL_STYPE addr = &p.To + } else if p.Mark&NEED_GOT_PCREL_ITYPE_RELOC == NEED_GOT_PCREL_ITYPE_RELOC { + rt = objabi.R_RISCV_GOT_PCREL_ITYPE + addr = &p.From } else { break } diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index 4feccf54f6bc2a..08c50ec72b6a5c 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -216,7 +216,7 @@ func (ctxt *Link) Int128Sym(hi, lo int64) *LSym { // GCLocalsSym generates a content-addressable sym containing data. func (ctxt *Link) GCLocalsSym(data []byte) *LSym { - sum := hash.Sum16(data) + sum := hash.Sum32(data) str := base64.StdEncoding.EncodeToString(sum[:16]) return ctxt.LookupInit(fmt.Sprintf("gclocals·%s", str), func(lsym *LSym) { lsym.P = data @@ -320,7 +320,7 @@ func (ctxt *Link) NumberSyms() { // Assign special index for builtin symbols. // Don't do it when linking against shared libraries, as the runtime // may be in a different library. - if i := goobj.BuiltinIdx(rs.Name, int(rs.ABI())); i != -1 { + if i := goobj.BuiltinIdx(rs.Name, int(rs.ABI())); i != -1 && !rs.IsLinkname() { rs.PkgIdx = goobj.PkgIdxBuiltin rs.SymIdx = int32(i) rs.Set(AttrIndexed, true) diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 26de22122a6269..7d87bff94918b4 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -591,6 +591,13 @@ type spcSet struct { var spcSpace []spcSet +// Each architecture is allotted a distinct subspace: [Lo, Hi) for declaring its +// arch-specific special operands. +const ( + SpecialOperandARM64Base = 0 << 16 + SpecialOperandRISCVBase = 1 << 16 +) + // RegisterSpecialOperands binds a pretty-printer (SPCconv) for special // operand numbers to a given special operand number range. Lo is inclusive, // hi is exclusive (valid special operands are lo through hi-1). diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go index 48eee4e3ea12de..42e5534f3b6254 100644 --- a/src/cmd/internal/obj/wasm/wasmobj.go +++ b/src/cmd/internal/obj/wasm/wasmobj.go @@ -129,6 +129,7 @@ var ( morestackNoCtxt *obj.LSym sigpanic *obj.LSym wasm_pc_f_loop_export *obj.LSym + runtimeNotInitialized *obj.LSym ) const ( @@ -149,6 +150,7 @@ func instinit(ctxt *obj.Link) { morestackNoCtxt = ctxt.Lookup("runtime.morestack_noctxt") sigpanic = ctxt.LookupABI("runtime.sigpanic", obj.ABIInternal) wasm_pc_f_loop_export = ctxt.Lookup("wasm_pc_f_loop_export") + runtimeNotInitialized = ctxt.Lookup("runtime.notInitialized") } func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { @@ -255,7 +257,7 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { p = appendp(p, AEnd) } - if framesize > 0 { + if framesize > 0 && s.Func().WasmExport == nil { // genWasmExportWrapper has its own prologue generation p := s.Func().Text p = appendp(p, AGet, regAddr(REG_SP)) p = appendp(p, AI32Const, constAddr(framesize)) @@ -935,6 +937,23 @@ func genWasmExportWrapper(s *obj.LSym, appendp func(p *obj.Prog, as obj.As, args panic("wrapper functions for WASM export should not have a body") } + // Detect and error out if called before runtime initialization + // SP is 0 if not initialized + p = appendp(p, AGet, regAddr(REG_SP)) + p = appendp(p, AI32Eqz) + p = appendp(p, AIf) + p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: runtimeNotInitialized}) + p = appendp(p, AEnd) + + // Now that we've checked the SP, generate the prologue + if framesize > 0 { + p = appendp(p, AGet, regAddr(REG_SP)) + p = appendp(p, AI32Const, constAddr(framesize)) + p = appendp(p, AI32Sub) + p = appendp(p, ASet, regAddr(REG_SP)) + p.Spadj = int32(framesize) + } + // Store args for i, f := range we.Params { p = appendp(p, AGet, regAddr(REG_SP)) @@ -1056,6 +1075,7 @@ var notUsePC_B = map[string]bool{ "runtime.gcWriteBarrier6": true, "runtime.gcWriteBarrier7": true, "runtime.gcWriteBarrier8": true, + "runtime.notInitialized": true, "runtime.wasmDiv": true, "runtime.wasmTruncS": true, "runtime.wasmTruncU": true, @@ -1121,7 +1141,8 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { "runtime.gcWriteBarrier5", "runtime.gcWriteBarrier6", "runtime.gcWriteBarrier7", - "runtime.gcWriteBarrier8": + "runtime.gcWriteBarrier8", + "runtime.notInitialized": // no locals useAssemblyRegMap() default: diff --git a/src/cmd/internal/objabi/path_test.go b/src/cmd/internal/objabi/path_test.go index 2f57882efad8b4..676f794292ab0d 100644 --- a/src/cmd/internal/objabi/path_test.go +++ b/src/cmd/internal/objabi/path_test.go @@ -12,24 +12,24 @@ import ( ) var escapeTests = []struct { - Path string - Escaped string - }{ - {"foo/bar/v1", "foo/bar/v1"}, - {"foo/bar/v.1", "foo/bar/v%2e1"}, - {"f.o.o/b.a.r/v1", "f.o.o/b.a.r/v1"}, - {"f.o.o/b.a.r/v.1", "f.o.o/b.a.r/v%2e1"}, - {"f.o.o/b.a.r/v..1", "f.o.o/b.a.r/v%2e%2e1"}, - {"f.o.o/b.a.r/v..1.", "f.o.o/b.a.r/v%2e%2e1%2e"}, - {"f.o.o/b.a.r/v%1", "f.o.o/b.a.r/v%251"}, - {"runtime", "runtime"}, - {"sync/atomic", "sync/atomic"}, - {"golang.org/x/tools/godoc", "golang.org/x/tools/godoc"}, - {"foo.bar/baz.quux", "foo.bar/baz%2equux"}, - {"", ""}, - {"%foo%bar", "%25foo%25bar"}, - {"\x01\x00\x7F☺", "%01%00%7f%e2%98%ba"}, - } + Path string + Escaped string +}{ + {"foo/bar/v1", "foo/bar/v1"}, + {"foo/bar/v.1", "foo/bar/v%2e1"}, + {"f.o.o/b.a.r/v1", "f.o.o/b.a.r/v1"}, + {"f.o.o/b.a.r/v.1", "f.o.o/b.a.r/v%2e1"}, + {"f.o.o/b.a.r/v..1", "f.o.o/b.a.r/v%2e%2e1"}, + {"f.o.o/b.a.r/v..1.", "f.o.o/b.a.r/v%2e%2e1%2e"}, + {"f.o.o/b.a.r/v%1", "f.o.o/b.a.r/v%251"}, + {"runtime", "runtime"}, + {"sync/atomic", "sync/atomic"}, + {"golang.org/x/tools/godoc", "golang.org/x/tools/godoc"}, + {"foo.bar/baz.quux", "foo.bar/baz%2equux"}, + {"", ""}, + {"%foo%bar", "%25foo%25bar"}, + {"\x01\x00\x7F☺", "%01%00%7f%e2%98%ba"}, +} func TestPathToPrefix(t *testing.T) { for _, tc := range escapeTests { diff --git a/src/cmd/internal/objabi/reloctype.go b/src/cmd/internal/objabi/reloctype.go index 9106b085ea7d02..8e9bee508224b2 100644 --- a/src/cmd/internal/objabi/reloctype.go +++ b/src/cmd/internal/objabi/reloctype.go @@ -291,6 +291,10 @@ const ( // address. R_RISCV_GOT_HI20 + // R_RISCV_GOT_PCREL_ITYPE resolves a 32-bit PC-relative GOT entry + // address for an AUIPC + I-type instruction pair. + R_RISCV_GOT_PCREL_ITYPE + // R_RISCV_PCREL_HI20 resolves the high 20 bits of a 32-bit PC-relative // address. R_RISCV_PCREL_HI20 diff --git a/src/cmd/internal/objabi/reloctype_string.go b/src/cmd/internal/objabi/reloctype_string.go index fd0e401db1e277..2d8a9554eb5c94 100644 --- a/src/cmd/internal/objabi/reloctype_string.go +++ b/src/cmd/internal/objabi/reloctype_string.go @@ -75,39 +75,40 @@ func _() { _ = x[R_RISCV_TLS_IE-65] _ = x[R_RISCV_TLS_LE-66] _ = x[R_RISCV_GOT_HI20-67] - _ = x[R_RISCV_PCREL_HI20-68] - _ = x[R_RISCV_PCREL_LO12_I-69] - _ = x[R_RISCV_PCREL_LO12_S-70] - _ = x[R_RISCV_BRANCH-71] - _ = x[R_RISCV_RVC_BRANCH-72] - _ = x[R_RISCV_RVC_JUMP-73] - _ = x[R_PCRELDBL-74] - _ = x[R_LOONG64_ADDR_HI-75] - _ = x[R_LOONG64_ADDR_LO-76] - _ = x[R_LOONG64_TLS_LE_HI-77] - _ = x[R_LOONG64_TLS_LE_LO-78] - _ = x[R_CALLLOONG64-79] - _ = x[R_LOONG64_TLS_IE_HI-80] - _ = x[R_LOONG64_TLS_IE_LO-81] - _ = x[R_LOONG64_GOT_HI-82] - _ = x[R_LOONG64_GOT_LO-83] - _ = x[R_LOONG64_ADD64-84] - _ = x[R_LOONG64_SUB64-85] - _ = x[R_JMP16LOONG64-86] - _ = x[R_JMP21LOONG64-87] - _ = x[R_JMPLOONG64-88] - _ = x[R_ADDRMIPSU-89] - _ = x[R_ADDRMIPSTLS-90] - _ = x[R_ADDRCUOFF-91] - _ = x[R_WASMIMPORT-92] - _ = x[R_XCOFFREF-93] - _ = x[R_PEIMAGEOFF-94] - _ = x[R_INITORDER-95] + _ = x[R_RISCV_GOT_PCREL_ITYPE-68] + _ = x[R_RISCV_PCREL_HI20-69] + _ = x[R_RISCV_PCREL_LO12_I-70] + _ = x[R_RISCV_PCREL_LO12_S-71] + _ = x[R_RISCV_BRANCH-72] + _ = x[R_RISCV_RVC_BRANCH-73] + _ = x[R_RISCV_RVC_JUMP-74] + _ = x[R_PCRELDBL-75] + _ = x[R_LOONG64_ADDR_HI-76] + _ = x[R_LOONG64_ADDR_LO-77] + _ = x[R_LOONG64_TLS_LE_HI-78] + _ = x[R_LOONG64_TLS_LE_LO-79] + _ = x[R_CALLLOONG64-80] + _ = x[R_LOONG64_TLS_IE_HI-81] + _ = x[R_LOONG64_TLS_IE_LO-82] + _ = x[R_LOONG64_GOT_HI-83] + _ = x[R_LOONG64_GOT_LO-84] + _ = x[R_LOONG64_ADD64-85] + _ = x[R_LOONG64_SUB64-86] + _ = x[R_JMP16LOONG64-87] + _ = x[R_JMP21LOONG64-88] + _ = x[R_JMPLOONG64-89] + _ = x[R_ADDRMIPSU-90] + _ = x[R_ADDRMIPSTLS-91] + _ = x[R_ADDRCUOFF-92] + _ = x[R_WASMIMPORT-93] + _ = x[R_XCOFFREF-94] + _ = x[R_PEIMAGEOFF-95] + _ = x[R_INITORDER-96] } -const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_LOONG64_ADDR_HIR_LOONG64_ADDR_LOR_LOONG64_TLS_LE_HIR_LOONG64_TLS_LE_LOR_CALLLOONG64R_LOONG64_TLS_IE_HIR_LOONG64_TLS_IE_LOR_LOONG64_GOT_HIR_LOONG64_GOT_LOR_LOONG64_ADD64R_LOONG64_SUB64R_JMP16LOONG64R_JMP21LOONG64R_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDER" +const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USENAMEDMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_PCREL_LDST8R_ARM64_PCREL_LDST16R_ARM64_PCREL_LDST32R_ARM64_PCREL_LDST64R_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_POWER_TLS_IE_PCREL34R_POWER_TLS_LE_TPREL34R_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_GOT_PCREL34R_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_ADDRPOWER_D34R_ADDRPOWER_PCREL34R_RISCV_JALR_RISCV_JAL_TRAMPR_RISCV_CALLR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IER_RISCV_TLS_LER_RISCV_GOT_HI20R_RISCV_GOT_PCREL_ITYPER_RISCV_PCREL_HI20R_RISCV_PCREL_LO12_IR_RISCV_PCREL_LO12_SR_RISCV_BRANCHR_RISCV_RVC_BRANCHR_RISCV_RVC_JUMPR_PCRELDBLR_LOONG64_ADDR_HIR_LOONG64_ADDR_LOR_LOONG64_TLS_LE_HIR_LOONG64_TLS_LE_LOR_CALLLOONG64R_LOONG64_TLS_IE_HIR_LOONG64_TLS_IE_LOR_LOONG64_GOT_HIR_LOONG64_GOT_LOR_LOONG64_ADD64R_LOONG64_SUB64R_JMP16LOONG64R_JMP21LOONG64R_JMPLOONG64R_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREFR_PEIMAGEOFFR_INITORDER" -var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 328, 344, 355, 368, 387, 407, 427, 447, 460, 474, 488, 502, 517, 531, 545, 556, 578, 600, 614, 629, 652, 669, 687, 708, 723, 742, 753, 770, 782, 801, 820, 834, 848, 864, 882, 902, 922, 936, 954, 970, 980, 997, 1014, 1033, 1052, 1065, 1084, 1103, 1119, 1135, 1150, 1165, 1179, 1193, 1205, 1216, 1229, 1240, 1252, 1262, 1274, 1285} +var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 226, 237, 243, 254, 264, 273, 286, 300, 314, 328, 344, 355, 368, 387, 407, 427, 447, 460, 474, 488, 502, 517, 531, 545, 556, 578, 600, 614, 629, 652, 669, 687, 708, 723, 742, 753, 770, 782, 801, 820, 834, 848, 864, 887, 905, 925, 945, 959, 977, 993, 1003, 1020, 1037, 1056, 1075, 1088, 1107, 1126, 1142, 1158, 1173, 1188, 1202, 1216, 1228, 1239, 1252, 1263, 1275, 1285, 1297, 1308} func (i RelocType) String() string { i -= 1 diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go index 2f2d7718132d57..ed9aae280e5579 100644 --- a/src/cmd/internal/objfile/objfile.go +++ b/src/cmd/internal/objfile/objfile.go @@ -119,10 +119,6 @@ func (f *File) DWARF() (*dwarf.Data, error) { return f.entries[0].DWARF() } -func (f *File) Disasm() (*Disasm, error) { - return f.entries[0].Disasm() -} - func (e *Entry) Name() string { return e.name } @@ -181,3 +177,9 @@ func (e *Entry) LoadAddress() (uint64, error) { func (e *Entry) DWARF() (*dwarf.Data, error) { return e.raw.dwarf() } + +type Liner interface { + // Given a pc, returns the corresponding file, line, and function data. + // If unknown, returns "",0,nil. + PCToLine(uint64) (string, int, *gosym.Func) +} diff --git a/src/cmd/internal/pgo/deserialize.go b/src/cmd/internal/pgo/deserialize.go index 4b075b8daf7fdd..dd26da2aa9929b 100644 --- a/src/cmd/internal/pgo/deserialize.go +++ b/src/cmd/internal/pgo/deserialize.go @@ -8,8 +8,8 @@ import ( "bufio" "fmt" "io" - "strings" "strconv" + "strings" ) // IsSerialized returns true if r is a serialized Profile. diff --git a/src/cmd/internal/pgo/pgo.go b/src/cmd/internal/pgo/pgo.go index 1d2cb880f744f8..3a0e01e8c27718 100644 --- a/src/cmd/internal/pgo/pgo.go +++ b/src/cmd/internal/pgo/pgo.go @@ -52,4 +52,3 @@ func emptyProfile() *Profile { func WeightInPercentage(value int64, total int64) float64 { return (float64(value) / float64(total)) * 100 } - diff --git a/src/cmd/internal/pgo/serialize_test.go b/src/cmd/internal/pgo/serialize_test.go index b24163d1e29432..9aef67c3670467 100644 --- a/src/cmd/internal/pgo/serialize_test.go +++ b/src/cmd/internal/pgo/serialize_test.go @@ -67,25 +67,25 @@ func TestRoundTrip(t *testing.T) { NamedEdgeMap: NamedEdgeMap{ ByWeight: []NamedCallEdge{ { - CallerName: "a", - CalleeName: "b", + CallerName: "a", + CalleeName: "b", CallSiteOffset: 14, }, { - CallerName: "c", - CalleeName: "d", + CallerName: "c", + CalleeName: "d", CallSiteOffset: 15, }, }, Weight: map[NamedCallEdge]int64{ { - CallerName: "a", - CalleeName: "b", + CallerName: "a", + CalleeName: "b", CallSiteOffset: 14, }: 2, { - CallerName: "c", - CalleeName: "d", + CallerName: "c", + CalleeName: "d", CallSiteOffset: 15, }: 1, }, @@ -157,8 +157,8 @@ func constructFuzzProfile(t *testing.T, b []byte) *Profile { } edge := NamedCallEdge{ - CallerName: caller, - CalleeName: callee, + CallerName: caller, + CalleeName: callee, CallSiteOffset: int(line), } diff --git a/src/cmd/internal/sys/arch.go b/src/cmd/internal/sys/arch.go index 2e35284137833a..ee7089b544f57a 100644 --- a/src/cmd/internal/sys/arch.go +++ b/src/cmd/internal/sys/arch.go @@ -208,7 +208,7 @@ var ArchPPC64 = &Arch{ RegSize: 8, MinLC: 4, Alignment: 1, - CanMergeLoads: false, + CanMergeLoads: true, HasLR: true, // PIC code on ppc64le requires 32 bytes of stack, and it's // easier to just use that much stack always. diff --git a/src/cmd/internal/testdir/testdir_test.go b/src/cmd/internal/testdir/testdir_test.go index 7469a6491aa4bb..29bd1f7cf81cb8 100644 --- a/src/cmd/internal/testdir/testdir_test.go +++ b/src/cmd/internal/testdir/testdir_test.go @@ -1489,7 +1489,7 @@ var ( "ppc64x": {}, // A pseudo-arch representing both ppc64 and ppc64le "s390x": {}, "wasm": {}, - "riscv64": {"GORISCV64", "rva20u64", "rva22u64"}, + "riscv64": {"GORISCV64", "rva20u64", "rva22u64", "rva23u64"}, } ) diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go index 9ec2c002f40e28..840f4b04edd1f4 100644 --- a/src/cmd/link/doc.go +++ b/src/cmd/link/doc.go @@ -72,6 +72,8 @@ Flags: system tools now assume the presence of the header. -dumpdep Dump symbol dependency graph. + -e + No limit on number of errors reported. -extar ar Set the external archive program (default "ar"). Used only for -buildmode=c-archive. @@ -118,6 +120,7 @@ Flags: Link with race detection libraries. -s Omit the symbol table and debug information. + Implies the -w flag, which can be negated with -w=0. -tmpdir dir Write temporary files to dir. Temporary files are only used in external linking mode. diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index b6eaf69ca48a95..a6b94a829f1442 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -55,17 +55,31 @@ import ( ) // isRuntimeDepPkg reports whether pkg is the runtime package or its dependency. +// TODO: just compute from the runtime package, and remove this hardcoded list. func isRuntimeDepPkg(pkg string) bool { switch pkg { case "runtime", - "sync/atomic", // runtime may call to sync/atomic, due to go:linkname - "internal/abi", // used by reflectcall (and maybe more) - "internal/bytealg", // for IndexByte + "sync/atomic", // runtime may call to sync/atomic, due to go:linkname // TODO: this is not true? + "internal/abi", // used by reflectcall (and maybe more) + "internal/asan", + "internal/bytealg", // for IndexByte + "internal/byteorder", "internal/chacha8rand", // for rand - "internal/cpu": // for cpu features + "internal/coverage/rtcov", + "internal/cpu", // for cpu features + "internal/goarch", + "internal/godebugs", + "internal/goexperiment", + "internal/goos", + "internal/msan", + "internal/profilerecord", + "internal/race", + "internal/stringslite", + "unsafe": return true } - return strings.HasPrefix(pkg, "runtime/internal/") && !strings.HasSuffix(pkg, "_test") + return (strings.HasPrefix(pkg, "runtime/internal/") || strings.HasPrefix(pkg, "internal/runtime/")) && + !strings.HasSuffix(pkg, "_test") } // Estimate the max size needed to hold any new trampolines created for this function. This @@ -410,6 +424,9 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) { // FIXME: It should be forbidden to have R_ADDR from a // symbol which isn't in .data. However, as .text has the // same address once loaded, this is possible. + // TODO: .text (including rodata) to .data relocation + // doesn't work correctly, so we should really disallow it. + // See also aixStaticDataBase in symtab.go and in runtime. if ldr.SymSect(s).Seg == &Segdata { Xcoffadddynrel(target, ldr, syms, s, r, ri) } diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index 14c0b687d853a3..b653e09a3c1b91 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -520,7 +520,7 @@ func (d *dwctxt) defgotype(gotype loader.Sym) loader.Sym { d.linkctxt.Errorf(gotype, "dwarf: type name doesn't start with \"type:\"") return d.mustFind("") } - name := sn[5:] // could also decode from Type.string + name := sn[len("type:"):] // could also decode from Type.string sdie := d.find(name) if sdie != 0 { @@ -534,7 +534,7 @@ func (d *dwctxt) defgotype(gotype loader.Sym) loader.Sym { func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie { sn := d.ldr.SymName(gotype) - name := sn[5:] // could also decode from Type.string + name := sn[len("type:"):] // could also decode from Type.string tdata := d.ldr.Data(gotype) if len(tdata) == 0 { d.linkctxt.Errorf(gotype, "missing type") diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index e6a525198fae25..6ff1d943833783 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1690,11 +1690,11 @@ func (ctxt *Link) doelf() { sb.SetType(sym.SRODATA) ldr.SetAttrSpecial(s, true) sb.SetReachable(true) - sb.SetSize(hash.Size20) + sb.SetSize(hash.Size32) slices.SortFunc(ctxt.Library, func(a, b *sym.Library) int { return strings.Compare(a.Pkg, b.Pkg) }) - h := hash.New20() + h := hash.New32() for _, l := range ctxt.Library { h.Write(l.Fingerprint[:]) } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 2d8f964f3594c6..b114ca2a3d4115 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1022,7 +1022,7 @@ func typeSymbolMangle(name string) string { return name } if isType { - hb := hash.Sum20([]byte(name[5:])) + hb := hash.Sum32([]byte(name[5:])) prefix := "type:" if name[5] == '.' { prefix = "type:." @@ -1035,7 +1035,7 @@ func typeSymbolMangle(name string) string { if j == -1 || j <= i { j = len(name) } - hb := hash.Sum20([]byte(name[i+1 : j])) + hb := hash.Sum32([]byte(name[i+1 : j])) return name[:i+1] + base64.StdEncoding.EncodeToString(hb[:6]) + name[j:] } diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 7614b6d194facf..377dcd6c856fa6 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -108,6 +108,7 @@ var ( flagEntrySymbol = flag.String("E", "", "set `entry` symbol name") flagPruneWeakMap = flag.Bool("pruneweakmap", true, "prune weak mapinit refs") flagRandLayout = flag.Int64("randlayout", 0, "randomize function layout") + flagAllErrors = flag.Bool("e", false, "no limit on number of errors reported") cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") memprofile = flag.String("memprofile", "", "write memory profile to `file`") memprofilerate = flag.Int64("memprofilerate", 0, "set runtime.MemProfileRate to `rate`") diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 8156f83a7a3267..b89a7802a2c62a 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -707,6 +707,17 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { // except go:buildid which is generated late and not used by the program. addRef("go:buildid") } + if ctxt.IsAIX() { + // On AIX, an R_ADDR relocation from an RODATA symbol to a DATA symbol + // does not work. See data.go:relocsym, case R_ADDR. + // Here we record the unrelocated address in aixStaticDataBase (it is + // unrelocated as it is in RODATA) so we can compute the delta at + // run time. + sb := ldr.CreateSymForUpdate("runtime.aixStaticDataBase", 0) + sb.SetSize(0) + sb.AddAddr(ctxt.Arch, ldr.Lookup("runtime.data", 0)) + sb.SetType(sym.SRODATA) + } // text section information slice(textsectionmapSym, uint64(nsections)) diff --git a/src/cmd/link/internal/ld/util.go b/src/cmd/link/internal/ld/util.go index 948bfa020e83f2..556c77d7326258 100644 --- a/src/cmd/link/internal/ld/util.go +++ b/src/cmd/link/internal/ld/util.go @@ -48,7 +48,7 @@ func afterErrorAction() { if *flagH { panic("error") } - if nerrors > 20 { + if nerrors > 20 && !*flagAllErrors { Exitf("too many errors") } } diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 6fe895a8409cc3..0c234e89758df3 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -2338,6 +2338,45 @@ var blockedLinknames = map[string][]string{ "runtime.newcoro": {"iter"}, // fips info "go:fipsinfo": {"crypto/internal/fips140/check"}, + // New internal linknames in Go 1.24 + // Pushed from runtime + "crypto/internal/fips140.fatal": {"crypto/internal/fips140"}, + "crypto/internal/fips140.getIndicator": {"crypto/internal/fips140"}, + "crypto/internal/fips140.setIndicator": {"crypto/internal/fips140"}, + "crypto/internal/sysrand.fatal": {"crypto/internal/sysrand"}, + "crypto/rand.fatal": {"crypto/rand"}, + "internal/runtime/maps.errNilAssign": {"internal/runtime/maps"}, + "internal/runtime/maps.fatal": {"internal/runtime/maps"}, + "internal/runtime/maps.mapKeyError": {"internal/runtime/maps"}, + "internal/runtime/maps.newarray": {"internal/runtime/maps"}, + "internal/runtime/maps.newobject": {"internal/runtime/maps"}, + "internal/runtime/maps.typedmemclr": {"internal/runtime/maps"}, + "internal/runtime/maps.typedmemmove": {"internal/runtime/maps"}, + "internal/sync.fatal": {"internal/sync"}, + "internal/sync.runtime_canSpin": {"internal/sync"}, + "internal/sync.runtime_doSpin": {"internal/sync"}, + "internal/sync.runtime_nanotime": {"internal/sync"}, + "internal/sync.runtime_Semrelease": {"internal/sync"}, + "internal/sync.runtime_SemacquireMutex": {"internal/sync"}, + "internal/sync.throw": {"internal/sync"}, + "internal/synctest.Run": {"internal/synctest"}, + "internal/synctest.Wait": {"internal/synctest"}, + "internal/synctest.acquire": {"internal/synctest"}, + "internal/synctest.release": {"internal/synctest"}, + "internal/synctest.inBubble": {"internal/synctest"}, + "runtime.getStaticuint64s": {"reflect"}, + "sync.runtime_SemacquireWaitGroup": {"sync"}, + "time.runtimeNow": {"time"}, + "time.runtimeNano": {"time"}, + // Pushed to runtime from internal/runtime/maps + // (other map functions are already linknamed in Go 1.23) + "runtime.mapaccess1": {"runtime"}, + "runtime.mapaccess1_fast32": {"runtime"}, + "runtime.mapaccess1_fast64": {"runtime"}, + "runtime.mapaccess1_faststr": {"runtime"}, + "runtime.mapdelete_fast32": {"runtime"}, + "runtime.mapdelete_fast64": {"runtime"}, + "runtime.mapdelete_faststr": {"runtime"}, } // check if a linkname reference to symbol s from pkg is allowed @@ -2355,6 +2394,16 @@ func (l *Loader) checkLinkname(pkg, name string, s Sym) { if pkg == p { return // pkg is allowed } + // crypto/internal/fips140/vX.Y.Z/... is the frozen version of + // crypto/internal/fips140/... and is similarly allowed. + if strings.HasPrefix(pkg, "crypto/internal/fips140/v") { + parts := strings.Split(pkg, "/") + parts = append(parts[:3], parts[4:]...) + pkg := strings.Join(parts, "/") + if pkg == p { + return + } + } } error() } diff --git a/src/cmd/link/internal/riscv64/asm.go b/src/cmd/link/internal/riscv64/asm.go index a3f50dc54fe22d..8e5d5be41ec0dd 100644 --- a/src/cmd/link/internal/riscv64/asm.go +++ b/src/cmd/link/internal/riscv64/asm.go @@ -170,8 +170,11 @@ func genSymsLate(ctxt *ld.Link, ldr *loader.Loader) { relocs := ldr.Relocs(s) for ri := 0; ri < relocs.Count(); ri++ { r := relocs.At(ri) - if r.Type() != objabi.R_RISCV_CALL && r.Type() != objabi.R_RISCV_PCREL_ITYPE && - r.Type() != objabi.R_RISCV_PCREL_STYPE && r.Type() != objabi.R_RISCV_TLS_IE { + if r.Type() != objabi.R_RISCV_CALL && + r.Type() != objabi.R_RISCV_PCREL_ITYPE && + r.Type() != objabi.R_RISCV_PCREL_STYPE && + r.Type() != objabi.R_RISCV_TLS_IE && + r.Type() != objabi.R_RISCV_GOT_PCREL_ITYPE { continue } if r.Off() == 0 && ldr.SymType(s).IsText() { @@ -233,7 +236,11 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, out.Write64(uint64(elf.R_RISCV_JAL) | uint64(elfsym)<<32) out.Write64(uint64(r.Xadd)) - case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE: + case objabi.R_RISCV_CALL, + objabi.R_RISCV_PCREL_ITYPE, + objabi.R_RISCV_PCREL_STYPE, + objabi.R_RISCV_TLS_IE, + objabi.R_RISCV_GOT_PCREL_ITYPE: // Find the text symbol for the AUIPC instruction targeted // by this relocation. relocs := ldr.Relocs(s) @@ -262,6 +269,8 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym, hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S case objabi.R_RISCV_TLS_IE: hiRel, loRel = elf.R_RISCV_TLS_GOT_HI20, elf.R_RISCV_PCREL_LO12_I + case objabi.R_RISCV_GOT_PCREL_ITYPE: + hiRel, loRel = elf.R_RISCV_GOT_HI20, elf.R_RISCV_PCREL_LO12_I } out.Write64(uint64(sectoff)) out.Write64(uint64(hiRel) | uint64(elfsym)<<32) @@ -426,7 +435,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade case objabi.R_RISCV_JAL, objabi.R_RISCV_JAL_TRAMP: return val, 1, true - case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE: + case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE, objabi.R_RISCV_GOT_PCREL_ITYPE: return val, 2, true } @@ -626,7 +635,7 @@ func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sy case objabi.R_RISCV_JAL, objabi.R_RISCV_JAL_TRAMP: return ld.ExtrelocSimple(ldr, r), true - case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE: + case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE, objabi.R_RISCV_GOT_PCREL_ITYPE: return ld.ExtrelocViaOuterSym(ldr, r, s), true } return loader.ExtReloc{}, false diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go index 727da59da630d4..d03102cc6be629 100644 --- a/src/cmd/link/internal/wasm/asm.go +++ b/src/cmd/link/internal/wasm/asm.go @@ -87,6 +87,7 @@ var wasmFuncTypes = map[string]*wasmFuncType{ "runtime.gcWriteBarrier6": {Results: []byte{I64}}, // -> bufptr "runtime.gcWriteBarrier7": {Results: []byte{I64}}, // -> bufptr "runtime.gcWriteBarrier8": {Results: []byte{I64}}, // -> bufptr + "runtime.notInitialized": {}, // "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1 "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1 "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0 diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go index f23951416ba8fd..ab56b49e15d3b1 100644 --- a/src/cmd/link/link_test.go +++ b/src/cmd/link/link_test.go @@ -1518,6 +1518,8 @@ func TestCheckLinkname(t *testing.T) { {"coro_asm", false}, // pull-only linkname is not ok {"coro2.go", false}, + // pull linkname of a builtin symbol is not ok + {"builtin.go", false}, // legacy bad linkname is ok, for now {"fastrand.go", true}, {"badlinkname.go", true}, diff --git a/src/cmd/link/testdata/linkname/builtin.go b/src/cmd/link/testdata/linkname/builtin.go new file mode 100644 index 00000000000000..a238c9b9679879 --- /dev/null +++ b/src/cmd/link/testdata/linkname/builtin.go @@ -0,0 +1,17 @@ +// Copyright 2024 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. + +// Linkname builtin symbols (that is not already linknamed, +// e.g. mapaccess1) is not allowed. + +package main + +import "unsafe" + +func main() { + mapaccess1(nil, nil, nil) +} + +//go:linkname mapaccess1 runtime.mapaccess1 +func mapaccess1(t, m, k unsafe.Pointer) unsafe.Pointer diff --git a/src/cmd/objdump/main.go b/src/cmd/objdump/main.go index b5b0d7f5178d5b..c98551e6b857b0 100644 --- a/src/cmd/objdump/main.go +++ b/src/cmd/objdump/main.go @@ -40,6 +40,7 @@ import ( "strconv" "strings" + "cmd/internal/disasm" "cmd/internal/objfile" "cmd/internal/telemetry/counter" ) @@ -82,7 +83,7 @@ func main() { } defer f.Close() - dis, err := f.Disasm() + dis, err := disasm.DisasmForFile(f) if err != nil { log.Fatalf("disassemble %s: %v", flag.Arg(0), err) } diff --git a/src/cmd/objdump/objdump_test.go b/src/cmd/objdump/objdump_test.go index 0f3a183c612d1c..0d6f608a3f8313 100644 --- a/src/cmd/objdump/objdump_test.go +++ b/src/cmd/objdump/objdump_test.go @@ -134,9 +134,9 @@ func testDisasm(t *testing.T, srcfname string, printCode bool, printGnuAsm bool, goarch = f[1] } - hash := hash.Sum16([]byte(fmt.Sprintf("%v-%v-%v-%v", srcfname, flags, printCode, printGnuAsm))) + hash := hash.Sum32([]byte(fmt.Sprintf("%v-%v-%v-%v", srcfname, flags, printCode, printGnuAsm))) tmp := t.TempDir() - hello := filepath.Join(tmp, fmt.Sprintf("hello-%x.exe", hash)) + hello := filepath.Join(tmp, fmt.Sprintf("hello-%x.exe", hash[:16])) args := []string{"build", "-o", hello} args = append(args, flags...) args = append(args, srcfname) diff --git a/src/cmd/pprof/pprof.go b/src/cmd/pprof/pprof.go index a1c2cd210f8dba..bfc2911b69cd39 100644 --- a/src/cmd/pprof/pprof.go +++ b/src/cmd/pprof/pprof.go @@ -24,6 +24,7 @@ import ( "sync" "time" + "cmd/internal/disasm" "cmd/internal/objfile" "cmd/internal/telemetry/counter" @@ -162,7 +163,7 @@ func adjustURL(source string, duration, timeout time.Duration) (string, time.Dur // (instead of invoking GNU binutils). type objTool struct { mu sync.Mutex - disasmCache map[string]*objfile.Disasm + disasmCache map[string]*disasm.Disasm } func (*objTool) Open(name string, start, limit, offset uint64, relocationSymbol string) (driver.ObjFile, error) { @@ -202,11 +203,11 @@ func (t *objTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]dr return asm, nil } -func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) { +func (t *objTool) cachedDisasm(file string) (*disasm.Disasm, error) { t.mu.Lock() defer t.mu.Unlock() if t.disasmCache == nil { - t.disasmCache = make(map[string]*objfile.Disasm) + t.disasmCache = make(map[string]*disasm.Disasm) } d := t.disasmCache[file] if d != nil { @@ -216,7 +217,7 @@ func (t *objTool) cachedDisasm(file string) (*objfile.Disasm, error) { if err != nil { return nil, err } - d, err = f.Disasm() + d, err = disasm.DisasmForFile(f) f.Close() if err != nil { return nil, err diff --git a/src/cmd/trace/gen.go b/src/cmd/trace/gen.go index 67811ca04d8678..6e4d82799e581d 100644 --- a/src/cmd/trace/gen.go +++ b/src/cmd/trace/gen.go @@ -248,7 +248,7 @@ func (g *globalRangeGenerator) GlobalRange(ctx *traceContext, ev *trace.Event) { Name: r.Name, Ts: ctx.elapsed(ar.time), Dur: ev.Time().Sub(ar.time), - Resource: trace.GCP, + Resource: traceviewer.GCP, Stack: ctx.Stack(viewerFrames(ar.stack)), EndStack: ctx.Stack(viewerFrames(ev.Stack())), }) @@ -267,7 +267,7 @@ func (g *globalRangeGenerator) Finish(ctx *traceContext) { Name: name, Ts: ctx.elapsed(ar.time), Dur: ctx.endTime.Sub(ar.time), - Resource: trace.GCP, + Resource: traceviewer.GCP, Stack: ctx.Stack(viewerFrames(ar.stack)), }) } @@ -282,11 +282,11 @@ func (g *globalMetricGenerator) GlobalMetric(ctx *traceContext, ev *trace.Event) m := ev.Metric() switch m.Name { case "/memory/classes/heap/objects:bytes": - ctx.HeapAlloc(ctx.elapsed(ev.Time()), m.Value.Uint64()) + ctx.HeapAlloc(ctx.elapsed(ev.Time()), m.Value.ToUint64()) case "/gc/heap/goal:bytes": - ctx.HeapGoal(ctx.elapsed(ev.Time()), m.Value.Uint64()) + ctx.HeapGoal(ctx.elapsed(ev.Time()), m.Value.ToUint64()) case "/sched/gomaxprocs:threads": - ctx.Gomaxprocs(m.Value.Uint64()) + ctx.Gomaxprocs(m.Value.ToUint64()) } } diff --git a/src/cmd/trace/gstate.go b/src/cmd/trace/gstate.go index c883166e06da2e..9c3da66217fd07 100644 --- a/src/cmd/trace/gstate.go +++ b/src/cmd/trace/gstate.go @@ -202,13 +202,13 @@ func (gs *gState[R]) syscallEnd(ts trace.Time, blocked bool, ctx *traceContext) // to emit a flow event from, indicating explicitly that this goroutine was unblocked by the system. func (gs *gState[R]) blockedSyscallEnd(ts trace.Time, stack trace.Stack, ctx *traceContext) { name := "exit blocked syscall" - gs.setStartCause(ts, name, trace.SyscallP, stack) + gs.setStartCause(ts, name, traceviewer.SyscallP, stack) // Emit an syscall exit instant event for the "Syscall" lane. ctx.Instant(traceviewer.InstantEvent{ Name: name, Ts: ctx.elapsed(ts), - Resource: trace.SyscallP, + Resource: traceviewer.SyscallP, Stack: ctx.Stack(viewerFrames(stack)), }) } @@ -228,7 +228,7 @@ func (gs *gState[R]) unblock(ts trace.Time, stack trace.Stack, resource R, ctx * // TODO(mknyszek): Handle this invalidness in a more general way. if _, ok := any(resource).(trace.ThreadID); !ok { // Emit an unblock instant event for the "Network" lane. - viewerResource = trace.NetpollP + viewerResource = traceviewer.NetpollP } ctx.Instant(traceviewer.InstantEvent{ Name: name, diff --git a/src/cmd/trace/jsontrace_test.go b/src/cmd/trace/jsontrace_test.go index 5f89b275dca197..c9df45040f7d17 100644 --- a/src/cmd/trace/jsontrace_test.go +++ b/src/cmd/trace/jsontrace_test.go @@ -7,7 +7,6 @@ package main import ( "bytes" "encoding/json" - "internal/trace" "io" "net/http/httptest" "os" @@ -19,6 +18,7 @@ import ( "time" "internal/trace/raw" + "internal/trace/traceviewer" "internal/trace/traceviewer/format" ) @@ -159,7 +159,7 @@ func checkNetworkUnblock(t *testing.T, data format.Data) { count := 0 var netBlockEv *format.Event for _, e := range data.Events { - if e.TID == trace.NetpollP && e.Name == "unblock (network)" && e.Phase == "I" && e.Scope == "t" { + if e.TID == traceviewer.NetpollP && e.Name == "unblock (network)" && e.Phase == "I" && e.Scope == "t" { count++ netBlockEv = e } diff --git a/src/cmd/trace/main.go b/src/cmd/trace/main.go index 075212eacb1490..7786e02d00b2a7 100644 --- a/src/cmd/trace/main.go +++ b/src/cmd/trace/main.go @@ -11,8 +11,8 @@ import ( "flag" "fmt" "internal/trace" - "internal/trace/event" "internal/trace/raw" + "internal/trace/tracev2" "internal/trace/traceviewer" "io" "log" @@ -372,13 +372,13 @@ func debugEventsFootprint(trc io.Reader) error { return err } type eventStats struct { - typ event.Type + typ tracev2.EventType count int bytes int } var stats [256]eventStats for i := range stats { - stats[i].typ = event.Type(i) + stats[i].typ = tracev2.EventType(i) } eventsRead := 0 for { diff --git a/src/cmd/trace/pprof.go b/src/cmd/trace/pprof.go index d27dfa7aa33100..a66419aedf764e 100644 --- a/src/cmd/trace/pprof.go +++ b/src/cmd/trace/pprof.go @@ -306,18 +306,15 @@ func (m *stackMap) profile() []traceviewer.ProfileRecord { prof := make([]traceviewer.ProfileRecord, 0, len(m.stacks)) for stack, record := range m.stacks { rec := *record - for i, frame := range slices.Collect(stack.Frames()) { - rec.Stack = append(rec.Stack, &trace.Frame{ - PC: frame.PC, - Fn: frame.Func, - File: frame.File, - Line: int(frame.Line), - }) + var i int + for frame := range stack.Frames() { + rec.Stack = append(rec.Stack, frame) // Cut this off at pprofMaxStack because that's as far // as our deduplication goes. if i >= pprofMaxStack { break } + i++ } prof = append(prof, rec) } diff --git a/src/cmd/trace/viewer.go b/src/cmd/trace/viewer.go index 6ce74b75b8dbba..da83e81ab9327e 100644 --- a/src/cmd/trace/viewer.go +++ b/src/cmd/trace/viewer.go @@ -8,22 +8,14 @@ import ( "fmt" "internal/trace" "internal/trace/traceviewer" + "slices" "time" ) // viewerFrames returns the frames of the stack of ev. The given frame slice is // used to store the frames to reduce allocations. -func viewerFrames(stk trace.Stack) []*trace.Frame { - var frames []*trace.Frame - for f := range stk.Frames() { - frames = append(frames, &trace.Frame{ - PC: f.PC, - Fn: f.Func, - File: f.File, - Line: int(f.Line), - }) - } - return frames +func viewerFrames(stk trace.Stack) []trace.StackFrame { + return slices.Collect(stk.Frames()) } func viewerGState(state trace.GoState, inMarkAssist bool) traceviewer.GState { diff --git a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js index 484c2d75908a4d..7db06996da499c 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js +++ b/src/cmd/vendor/github.com/google/pprof/internal/driver/html/stacks.js @@ -135,7 +135,9 @@ function stackViewer(stacks, nodes) { } // Update params to include src. - let v = pprofQuoteMeta(stacks.Sources[src].FullName); + // When `pprof` is invoked with `-lines`, FullName will be suffixed with `:`, + // which we need to remove. + let v = pprofQuoteMeta(stacks.Sources[src].FullName.replace(/:[0-9]+$/, '')); if (param != 'f' && param != 'sf') { // old f,sf values are overwritten // Add new source to current parameter value. const old = params.get(param); diff --git a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go index 8e73f179ecd09b..9d52872b7d4383 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/report/report.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/report/report.go @@ -699,13 +699,17 @@ func printTags(w io.Writer, rpt *Report) error { p := rpt.prof o := rpt.options - formatTag := func(v int64, key string) string { - return measurement.ScaledLabel(v, key, o.OutputUnit) + formatTag := func(v int64, unit string) string { + return measurement.ScaledLabel(v, unit, o.OutputUnit) } - // Hashtable to keep accumulate tags as key,value,count. + // Accumulate tags as key,value,count. tagMap := make(map[string]map[string]int64) + // Note that we assume single value per tag per sample. Multiple values are + // encodable in the format but are discouraged. + tagTotalMap := make(map[string]int64) for _, s := range p.Sample { + sampleValue := o.SampleValue(s.Value) for key, vals := range s.Label { for _, val := range vals { valueMap, ok := tagMap[key] @@ -713,7 +717,8 @@ func printTags(w io.Writer, rpt *Report) error { valueMap = make(map[string]int64) tagMap[key] = valueMap } - valueMap[val] += o.SampleValue(s.Value) + valueMap[val] += sampleValue + tagTotalMap[key] += sampleValue } } for key, vals := range s.NumLabel { @@ -725,7 +730,8 @@ func printTags(w io.Writer, rpt *Report) error { valueMap = make(map[string]int64) tagMap[key] = valueMap } - valueMap[val] += o.SampleValue(s.Value) + valueMap[val] += sampleValue + tagTotalMap[key] += sampleValue } } } @@ -736,22 +742,23 @@ func printTags(w io.Writer, rpt *Report) error { } tabw := tabwriter.NewWriter(w, 0, 0, 1, ' ', tabwriter.AlignRight) for _, tagKey := range graph.SortTags(tagKeys, true) { - var total int64 key := tagKey.Name tags := make([]*graph.Tag, 0, len(tagMap[key])) for t, c := range tagMap[key] { - total += c tags = append(tags, &graph.Tag{Name: t, Flat: c}) } - f, u := measurement.Scale(total, o.SampleUnit, o.OutputUnit) - fmt.Fprintf(tabw, "%s:\t Total %.1f%s\n", key, f, u) + tagTotal, profileTotal := tagTotalMap[key], rpt.Total() + if profileTotal > 0 { + fmt.Fprintf(tabw, "%s:\t Total %s of %s (%s)\n", key, rpt.formatValue(tagTotal), rpt.formatValue(profileTotal), measurement.Percentage(tagTotal, profileTotal)) + } else { + fmt.Fprintf(tabw, "%s:\t Total %s of %s\n", key, rpt.formatValue(tagTotal), rpt.formatValue(profileTotal)) + } for _, t := range graph.SortTags(tags, true) { - f, u := measurement.Scale(t.FlatValue(), o.SampleUnit, o.OutputUnit) - if total > 0 { - fmt.Fprintf(tabw, " \t%.1f%s (%s):\t %s\n", f, u, measurement.Percentage(t.FlatValue(), total), t.Name) + if profileTotal > 0 { + fmt.Fprintf(tabw, " \t%s (%s):\t %s\n", rpt.formatValue(t.FlatValue()), measurement.Percentage(t.FlatValue(), profileTotal), t.Name) } else { - fmt.Fprintf(tabw, " \t%.1f%s:\t %s\n", f, u, t.Name) + fmt.Fprintf(tabw, " \t%s:\t %s\n", rpt.formatValue(t.FlatValue()), t.Name) } } fmt.Fprintln(tabw) diff --git a/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go b/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go index 0d451364619381..95c15b136655c7 100644 --- a/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go +++ b/src/cmd/vendor/github.com/google/pprof/internal/symbolizer/symbolizer.go @@ -257,6 +257,10 @@ func Demangle(prof *profile.Profile, force bool, demanglerMode string) { } options := demanglerModeToOptions(demanglerMode) + // Bail out fast to avoid any parsing, if we really don't want any demangling. + if len(options) == 0 { + return + } for _, fn := range prof.Function { demangleSingleFunction(fn, options) } @@ -288,6 +292,16 @@ func demangleSingleFunction(fn *profile.Function, options []demangle.Option) { fn.Name = demangled return } + + // OSX has all the symbols prefixed with extra '_' so lets try + // once more without it + if strings.HasPrefix(fn.SystemName, "_") { + if demangled := demangle.Filter(fn.SystemName[1:], o...); demangled != fn.SystemName { + fn.Name = demangled + return + } + } + // Could not demangle. Apply heuristics in case the name is // already demangled. name := fn.SystemName diff --git a/src/cmd/vendor/golang.org/x/arch/riscv64/riscv64asm/gnu.go b/src/cmd/vendor/golang.org/x/arch/riscv64/riscv64asm/gnu.go index d6b3dc040e8bcc..3ee0449640aa5a 100644 --- a/src/cmd/vendor/golang.org/x/arch/riscv64/riscv64asm/gnu.go +++ b/src/cmd/vendor/golang.org/x/arch/riscv64/riscv64asm/gnu.go @@ -42,6 +42,11 @@ func GNUSyntax(inst Inst) string { } } + if inst.Op == ANDI && inst.Args[2].(Simm).Imm == 255 { + op = "zext.b" + args = args[:len(args)-1] + } + if inst.Op == ADDIW && inst.Args[2].(Simm).Imm == 0 { op = "sext.w" args = args[:len(args)-1] diff --git a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go index 6f57c70bf1529a..9710bbd8bd0c5b 100644 --- a/src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go +++ b/src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go @@ -1,4 +1,4 @@ -// Code generated by x86map -fmt=decoder x86.csv DO NOT EDIT. +// Code generated by x86map -fmt=decoder ../x86.csv DO NOT EDIT. package x86asm diff --git a/src/cmd/vendor/golang.org/x/build/relnote/links.go b/src/cmd/vendor/golang.org/x/build/relnote/links.go index b8e3a0f4b08f16..ff62cdabfa3c5f 100644 --- a/src/cmd/vendor/golang.org/x/build/relnote/links.go +++ b/src/cmd/vendor/golang.org/x/build/relnote/links.go @@ -158,7 +158,7 @@ func symbolLinkText(i int, ins []md.Inline) string { if plainText(i) != "[" { return "" } - // The open bracket must be preceeded by a link-adjacent rune (or by nothing). + // The open bracket must be preceded by a link-adjacent rune (or by nothing). if t := plainText(i - 1); t != "" { r, _ := utf8.DecodeLastRuneInString(t) if !isLinkAdjacentRune(r) { diff --git a/src/cmd/vendor/golang.org/x/build/relnote/relnote.go b/src/cmd/vendor/golang.org/x/build/relnote/relnote.go index ba48e6f576fc2a..eeb12ce9e3ea28 100644 --- a/src/cmd/vendor/golang.org/x/build/relnote/relnote.go +++ b/src/cmd/vendor/golang.org/x/build/relnote/relnote.go @@ -462,5 +462,8 @@ func checkFragmentFile(fsys fs.FS, filename string) error { } defer f.Close() data, err := io.ReadAll(f) + if err != nil { + return err + } return CheckFragment(string(data)) } diff --git a/src/cmd/vendor/golang.org/x/mod/modfile/read.go b/src/cmd/vendor/golang.org/x/mod/modfile/read.go index de1b98211a1907..2d7486804f5060 100644 --- a/src/cmd/vendor/golang.org/x/mod/modfile/read.go +++ b/src/cmd/vendor/golang.org/x/mod/modfile/read.go @@ -877,6 +877,11 @@ func (in *input) parseLineBlock(start Position, token []string, lparen token) *L in.Error(fmt.Sprintf("syntax error (unterminated block started at %s:%d:%d)", in.filename, x.Start.Line, x.Start.LineRune)) case ')': rparen := in.lex() + // Don't preserve blank lines (denoted by a single empty comment, added above) + // at the end of the block. + if len(comments) == 1 && comments[0] == (Comment{}) { + comments = nil + } x.RParen.Before = comments x.RParen.Pos = rparen.pos if !in.peek().isEOL() { diff --git a/src/cmd/vendor/golang.org/x/sync/errgroup/errgroup.go b/src/cmd/vendor/golang.org/x/sync/errgroup/errgroup.go index 948a3ee63d4ffe..b8322598ae3ea0 100644 --- a/src/cmd/vendor/golang.org/x/sync/errgroup/errgroup.go +++ b/src/cmd/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -118,6 +118,7 @@ func (g *Group) TryGo(f func() error) bool { // SetLimit limits the number of active goroutines in this group to at most n. // A negative value indicates no limit. +// A limit of zero will prevent any new goroutines from being added. // // Any subsequent call to the Go method will block until it can add an active // goroutine without exceeding the configured limit. diff --git a/src/cmd/vendor/golang.org/x/sys/unix/auxv.go b/src/cmd/vendor/golang.org/x/sys/unix/auxv.go new file mode 100644 index 00000000000000..37a82528f580f1 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/sys/unix/auxv.go @@ -0,0 +1,36 @@ +// Copyright 2025 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. + +//go:build go1.21 && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) + +package unix + +import ( + "syscall" + "unsafe" +) + +//go:linkname runtime_getAuxv runtime.getAuxv +func runtime_getAuxv() []uintptr + +// Auxv returns the ELF auxiliary vector as a sequence of key/value pairs. +// The returned slice is always a fresh copy, owned by the caller. +// It returns an error on non-ELF platforms, or if the auxiliary vector cannot be accessed, +// which happens in some locked-down environments and build modes. +func Auxv() ([][2]uintptr, error) { + vec := runtime_getAuxv() + vecLen := len(vec) + + if vecLen == 0 { + return nil, syscall.ENOENT + } + + if vecLen%2 != 0 { + return nil, syscall.EINVAL + } + + result := make([]uintptr, vecLen) + copy(result, vec) + return unsafe.Slice((*[2]uintptr)(unsafe.Pointer(&result[0])), vecLen/2), nil +} diff --git a/src/cmd/vendor/golang.org/x/sys/unix/auxv_unsupported.go b/src/cmd/vendor/golang.org/x/sys/unix/auxv_unsupported.go new file mode 100644 index 00000000000000..1200487f2e86c6 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/sys/unix/auxv_unsupported.go @@ -0,0 +1,13 @@ +// Copyright 2025 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. + +//go:build !go1.21 && (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) + +package unix + +import "syscall" + +func Auxv() ([][2]uintptr, error) { + return nil, syscall.ENOTSUP +} diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_dragonfly.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_dragonfly.go index 97cb916f2c90ef..be8c0020701ee4 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_dragonfly.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_dragonfly.go @@ -246,6 +246,18 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e return sendfile(outfd, infd, offset, count) } +func Dup3(oldfd, newfd, flags int) error { + if oldfd == newfd || flags&^O_CLOEXEC != 0 { + return EINVAL + } + how := F_DUP2FD + if flags&O_CLOEXEC != 0 { + how = F_DUP2FD_CLOEXEC + } + _, err := fcntl(oldfd, how, newfd) + return err +} + /* * Exposed directly */ diff --git a/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go b/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go index 21974af064ddc3..abc3955477c7d3 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/syscall_solaris.go @@ -1102,3 +1102,90 @@ func (s *Strioctl) SetInt(i int) { func IoctlSetStrioctlRetInt(fd int, req int, s *Strioctl) (int, error) { return ioctlPtrRet(fd, req, unsafe.Pointer(s)) } + +// Ucred Helpers +// See ucred(3c) and getpeerucred(3c) + +//sys getpeerucred(fd uintptr, ucred *uintptr) (err error) +//sys ucredFree(ucred uintptr) = ucred_free +//sys ucredGet(pid int) (ucred uintptr, err error) = ucred_get +//sys ucredGeteuid(ucred uintptr) (uid int) = ucred_geteuid +//sys ucredGetegid(ucred uintptr) (gid int) = ucred_getegid +//sys ucredGetruid(ucred uintptr) (uid int) = ucred_getruid +//sys ucredGetrgid(ucred uintptr) (gid int) = ucred_getrgid +//sys ucredGetsuid(ucred uintptr) (uid int) = ucred_getsuid +//sys ucredGetsgid(ucred uintptr) (gid int) = ucred_getsgid +//sys ucredGetpid(ucred uintptr) (pid int) = ucred_getpid + +// Ucred is an opaque struct that holds user credentials. +type Ucred struct { + ucred uintptr +} + +// We need to ensure that ucredFree is called on the underlying ucred +// when the Ucred is garbage collected. +func ucredFinalizer(u *Ucred) { + ucredFree(u.ucred) +} + +func GetPeerUcred(fd uintptr) (*Ucred, error) { + var ucred uintptr + err := getpeerucred(fd, &ucred) + if err != nil { + return nil, err + } + result := &Ucred{ + ucred: ucred, + } + // set the finalizer on the result so that the ucred will be freed + runtime.SetFinalizer(result, ucredFinalizer) + return result, nil +} + +func UcredGet(pid int) (*Ucred, error) { + ucred, err := ucredGet(pid) + if err != nil { + return nil, err + } + result := &Ucred{ + ucred: ucred, + } + // set the finalizer on the result so that the ucred will be freed + runtime.SetFinalizer(result, ucredFinalizer) + return result, nil +} + +func (u *Ucred) Geteuid() int { + defer runtime.KeepAlive(u) + return ucredGeteuid(u.ucred) +} + +func (u *Ucred) Getruid() int { + defer runtime.KeepAlive(u) + return ucredGetruid(u.ucred) +} + +func (u *Ucred) Getsuid() int { + defer runtime.KeepAlive(u) + return ucredGetsuid(u.ucred) +} + +func (u *Ucred) Getegid() int { + defer runtime.KeepAlive(u) + return ucredGetegid(u.ucred) +} + +func (u *Ucred) Getrgid() int { + defer runtime.KeepAlive(u) + return ucredGetrgid(u.ucred) +} + +func (u *Ucred) Getsgid() int { + defer runtime.KeepAlive(u) + return ucredGetsgid(u.ucred) +} + +func (u *Ucred) Getpid() int { + defer runtime.KeepAlive(u) + return ucredGetpid(u.ucred) +} diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go index 6ebc48b3fecd71..4f432bfe8feeee 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux.go @@ -1245,6 +1245,7 @@ const ( FAN_REPORT_DFID_NAME = 0xc00 FAN_REPORT_DFID_NAME_TARGET = 0x1e00 FAN_REPORT_DIR_FID = 0x400 + FAN_REPORT_FD_ERROR = 0x2000 FAN_REPORT_FID = 0x200 FAN_REPORT_NAME = 0x800 FAN_REPORT_PIDFD = 0x80 @@ -1330,8 +1331,10 @@ const ( FUSE_SUPER_MAGIC = 0x65735546 FUTEXFS_SUPER_MAGIC = 0xbad1dea F_ADD_SEALS = 0x409 + F_CREATED_QUERY = 0x404 F_DUPFD = 0x0 F_DUPFD_CLOEXEC = 0x406 + F_DUPFD_QUERY = 0x403 F_EXLCK = 0x4 F_GETFD = 0x1 F_GETFL = 0x3 @@ -1551,6 +1554,7 @@ const ( IPPROTO_ROUTING = 0x2b IPPROTO_RSVP = 0x2e IPPROTO_SCTP = 0x84 + IPPROTO_SMC = 0x100 IPPROTO_TCP = 0x6 IPPROTO_TP = 0x1d IPPROTO_UDP = 0x11 @@ -1623,6 +1627,8 @@ const ( IPV6_UNICAST_IF = 0x4c IPV6_USER_FLOW = 0xe IPV6_V6ONLY = 0x1a + IPV6_VERSION = 0x60 + IPV6_VERSION_MASK = 0xf0 IPV6_XFRM_POLICY = 0x23 IP_ADD_MEMBERSHIP = 0x23 IP_ADD_SOURCE_MEMBERSHIP = 0x27 @@ -1867,6 +1873,7 @@ const ( MADV_UNMERGEABLE = 0xd MADV_WILLNEED = 0x3 MADV_WIPEONFORK = 0x12 + MAP_DROPPABLE = 0x8 MAP_FILE = 0x0 MAP_FIXED = 0x10 MAP_FIXED_NOREPLACE = 0x100000 @@ -1967,6 +1974,7 @@ const ( MSG_PEEK = 0x2 MSG_PROXY = 0x10 MSG_RST = 0x1000 + MSG_SOCK_DEVMEM = 0x2000000 MSG_SYN = 0x400 MSG_TRUNC = 0x20 MSG_TRYHARD = 0x4 @@ -2083,6 +2091,7 @@ const ( NFC_ATR_REQ_MAXSIZE = 0x40 NFC_ATR_RES_GB_MAXSIZE = 0x2f NFC_ATR_RES_MAXSIZE = 0x40 + NFC_ATS_MAXSIZE = 0x14 NFC_COMM_ACTIVE = 0x0 NFC_COMM_PASSIVE = 0x1 NFC_DEVICE_NAME_MAXSIZE = 0x8 @@ -2163,6 +2172,7 @@ const ( NFNL_SUBSYS_QUEUE = 0x3 NFNL_SUBSYS_ULOG = 0x4 NFS_SUPER_MAGIC = 0x6969 + NFT_BITWISE_BOOL = 0x0 NFT_CHAIN_FLAGS = 0x7 NFT_CHAIN_MAXNAMELEN = 0x100 NFT_CT_MAX = 0x17 @@ -2491,6 +2501,7 @@ const ( PR_GET_PDEATHSIG = 0x2 PR_GET_SECCOMP = 0x15 PR_GET_SECUREBITS = 0x1b + PR_GET_SHADOW_STACK_STATUS = 0x4a PR_GET_SPECULATION_CTRL = 0x34 PR_GET_TAGGED_ADDR_CTRL = 0x38 PR_GET_THP_DISABLE = 0x2a @@ -2499,6 +2510,7 @@ const ( PR_GET_TIMING = 0xd PR_GET_TSC = 0x19 PR_GET_UNALIGN = 0x5 + PR_LOCK_SHADOW_STACK_STATUS = 0x4c PR_MCE_KILL = 0x21 PR_MCE_KILL_CLEAR = 0x0 PR_MCE_KILL_DEFAULT = 0x2 @@ -2525,6 +2537,8 @@ const ( PR_PAC_GET_ENABLED_KEYS = 0x3d PR_PAC_RESET_KEYS = 0x36 PR_PAC_SET_ENABLED_KEYS = 0x3c + PR_PMLEN_MASK = 0x7f000000 + PR_PMLEN_SHIFT = 0x18 PR_PPC_DEXCR_CTRL_CLEAR = 0x4 PR_PPC_DEXCR_CTRL_CLEAR_ONEXEC = 0x10 PR_PPC_DEXCR_CTRL_EDITABLE = 0x1 @@ -2592,6 +2606,7 @@ const ( PR_SET_PTRACER = 0x59616d61 PR_SET_SECCOMP = 0x16 PR_SET_SECUREBITS = 0x1c + PR_SET_SHADOW_STACK_STATUS = 0x4b PR_SET_SPECULATION_CTRL = 0x35 PR_SET_SYSCALL_USER_DISPATCH = 0x3b PR_SET_TAGGED_ADDR_CTRL = 0x37 @@ -2602,6 +2617,9 @@ const ( PR_SET_UNALIGN = 0x6 PR_SET_VMA = 0x53564d41 PR_SET_VMA_ANON_NAME = 0x0 + PR_SHADOW_STACK_ENABLE = 0x1 + PR_SHADOW_STACK_PUSH = 0x4 + PR_SHADOW_STACK_WRITE = 0x2 PR_SME_GET_VL = 0x40 PR_SME_SET_VL = 0x3f PR_SME_SET_VL_ONEXEC = 0x40000 @@ -2911,7 +2929,6 @@ const ( RTM_NEWNEXTHOP = 0x68 RTM_NEWNEXTHOPBUCKET = 0x74 RTM_NEWNSID = 0x58 - RTM_NEWNVLAN = 0x70 RTM_NEWPREFIX = 0x34 RTM_NEWQDISC = 0x24 RTM_NEWROUTE = 0x18 @@ -2920,6 +2937,7 @@ const ( RTM_NEWTCLASS = 0x28 RTM_NEWTFILTER = 0x2c RTM_NEWTUNNEL = 0x78 + RTM_NEWVLAN = 0x70 RTM_NR_FAMILIES = 0x1b RTM_NR_MSGTYPES = 0x6c RTM_SETDCB = 0x4f diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_386.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_386.go index c0d45e320505ff..75207613c785db 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_386.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_386.go @@ -116,6 +116,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -304,6 +306,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go index c731d24f025291..c68acda53522d1 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_amd64.go @@ -116,6 +116,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -305,6 +307,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go index 680018a4a7c9f0..a8c607ab86b51b 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -310,6 +312,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go index a63909f308d6d7..18563dd8d33a0f 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_arm64.go @@ -109,6 +109,7 @@ const ( F_SETOWN = 0x8 F_UNLCK = 0x2 F_WRLCK = 0x1 + GCS_MAGIC = 0x47435300 HIDIOCGRAWINFO = 0x80084803 HIDIOCGRDESC = 0x90044802 HIDIOCGRDESCSIZE = 0x80044801 @@ -119,6 +120,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -302,6 +305,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go index 9b0a2573fe3fb3..22912cdaa94483 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_loong64.go @@ -116,6 +116,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -297,6 +299,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go index 958e6e0645acdd..29344eb37ab55a 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xfffffff + IPV6_FLOWLABEL_MASK = 0xfffff ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -303,6 +305,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go index 50c7f25bd16c6b..20d51fb96a897f 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xfffffff + IPV6_FLOWLABEL_MASK = 0xfffff ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -303,6 +305,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go index ced21d66d955aa..321b60902ae5cd 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mips64le.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -303,6 +305,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go index 226c0441902358..9bacdf1e27910f 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_mipsle.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x80 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -303,6 +305,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go index 3122737cd464f0..c2242726156a94 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xfffffff + IPV6_FLOWLABEL_MASK = 0xfffff ISIG = 0x80 IUCLC = 0x1000 IXOFF = 0x400 @@ -358,6 +360,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go index eb5d3467edf0c1..6270c8ee13e3f5 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xfffffff + IPV6_FLOWLABEL_MASK = 0xfffff ISIG = 0x80 IUCLC = 0x1000 IXOFF = 0x400 @@ -362,6 +364,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go index e921ebc60b7142..9966c1941f8301 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_ppc64le.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x80 IUCLC = 0x1000 IXOFF = 0x400 @@ -362,6 +364,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go index 38ba81c55c1fd3..848e5fcc42e6f2 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_riscv64.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + IPV6_FLOWINFO_MASK = 0xffffff0f + IPV6_FLOWLABEL_MASK = 0xffff0f00 ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -294,6 +296,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go index 71f0400977b367..669b2adb80b778 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_s390x.go @@ -115,6 +115,8 @@ const ( IN_CLOEXEC = 0x80000 IN_NONBLOCK = 0x800 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x7b9 + IPV6_FLOWINFO_MASK = 0xfffffff + IPV6_FLOWLABEL_MASK = 0xfffff ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -366,6 +368,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x36 SCM_TIMESTAMPING_PKTINFO = 0x3a SCM_TIMESTAMPNS = 0x23 + SCM_TS_OPT_ID = 0x51 SCM_TXTIME = 0x3d SCM_WIFI_STATUS = 0x29 SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go index c44a313322c54c..4834e57514e44a 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zerrors_linux_sparc64.go @@ -119,6 +119,8 @@ const ( IN_CLOEXEC = 0x400000 IN_NONBLOCK = 0x4000 IOCTL_VM_SOCKETS_GET_LOCAL_CID = 0x200007b9 + IPV6_FLOWINFO_MASK = 0xfffffff + IPV6_FLOWLABEL_MASK = 0xfffff ISIG = 0x1 IUCLC = 0x200 IXOFF = 0x1000 @@ -357,6 +359,7 @@ const ( SCM_TIMESTAMPING_OPT_STATS = 0x38 SCM_TIMESTAMPING_PKTINFO = 0x3c SCM_TIMESTAMPNS = 0x21 + SCM_TS_OPT_ID = 0x5a SCM_TXTIME = 0x3f SCM_WIFI_STATUS = 0x25 SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103 diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go index 829b87feb8da62..c6545413c45b44 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go @@ -141,6 +141,16 @@ import ( //go:cgo_import_dynamic libc_getpeername getpeername "libsocket.so" //go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so" //go:cgo_import_dynamic libc_recvfrom recvfrom "libsocket.so" +//go:cgo_import_dynamic libc_getpeerucred getpeerucred "libc.so" +//go:cgo_import_dynamic libc_ucred_get ucred_get "libc.so" +//go:cgo_import_dynamic libc_ucred_geteuid ucred_geteuid "libc.so" +//go:cgo_import_dynamic libc_ucred_getegid ucred_getegid "libc.so" +//go:cgo_import_dynamic libc_ucred_getruid ucred_getruid "libc.so" +//go:cgo_import_dynamic libc_ucred_getrgid ucred_getrgid "libc.so" +//go:cgo_import_dynamic libc_ucred_getsuid ucred_getsuid "libc.so" +//go:cgo_import_dynamic libc_ucred_getsgid ucred_getsgid "libc.so" +//go:cgo_import_dynamic libc_ucred_getpid ucred_getpid "libc.so" +//go:cgo_import_dynamic libc_ucred_free ucred_free "libc.so" //go:cgo_import_dynamic libc_port_create port_create "libc.so" //go:cgo_import_dynamic libc_port_associate port_associate "libc.so" //go:cgo_import_dynamic libc_port_dissociate port_dissociate "libc.so" @@ -280,6 +290,16 @@ import ( //go:linkname procgetpeername libc_getpeername //go:linkname procsetsockopt libc_setsockopt //go:linkname procrecvfrom libc_recvfrom +//go:linkname procgetpeerucred libc_getpeerucred +//go:linkname procucred_get libc_ucred_get +//go:linkname procucred_geteuid libc_ucred_geteuid +//go:linkname procucred_getegid libc_ucred_getegid +//go:linkname procucred_getruid libc_ucred_getruid +//go:linkname procucred_getrgid libc_ucred_getrgid +//go:linkname procucred_getsuid libc_ucred_getsuid +//go:linkname procucred_getsgid libc_ucred_getsgid +//go:linkname procucred_getpid libc_ucred_getpid +//go:linkname procucred_free libc_ucred_free //go:linkname procport_create libc_port_create //go:linkname procport_associate libc_port_associate //go:linkname procport_dissociate libc_port_dissociate @@ -420,6 +440,16 @@ var ( procgetpeername, procsetsockopt, procrecvfrom, + procgetpeerucred, + procucred_get, + procucred_geteuid, + procucred_getegid, + procucred_getruid, + procucred_getrgid, + procucred_getsuid, + procucred_getsgid, + procucred_getpid, + procucred_free, procport_create, procport_associate, procport_dissociate, @@ -2029,6 +2059,90 @@ func recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Sockl // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func getpeerucred(fd uintptr, ucred *uintptr) (err error) { + _, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procgetpeerucred)), 2, uintptr(fd), uintptr(unsafe.Pointer(ucred)), 0, 0, 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGet(pid int) (ucred uintptr, err error) { + r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procucred_get)), 1, uintptr(pid), 0, 0, 0, 0, 0) + ucred = uintptr(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGeteuid(ucred uintptr) (uid int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&procucred_geteuid)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGetegid(ucred uintptr) (gid int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&procucred_getegid)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGetruid(ucred uintptr) (uid int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&procucred_getruid)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGetrgid(ucred uintptr) (gid int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&procucred_getrgid)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGetsuid(ucred uintptr) (uid int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&procucred_getsuid)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + uid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGetsgid(ucred uintptr) (gid int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&procucred_getsgid)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + gid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredGetpid(ucred uintptr) (pid int) { + r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&procucred_getpid)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + pid = int(r0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + +func ucredFree(ucred uintptr) { + sysvicall6(uintptr(unsafe.Pointer(&procucred_free)), 1, uintptr(ucred), 0, 0, 0, 0, 0) + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func port_create() (n int, err error) { r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&procport_create)), 0, 0, 0, 0, 0, 0, 0) n = int(r0) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go index 524b0820cbc2ee..c79aaff306ae3e 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_386.go @@ -458,4 +458,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go index f485dbf4565671..5eb450695e95a8 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_amd64.go @@ -381,4 +381,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go index 70b35bf3b09f68..05e50297445861 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm.go @@ -422,4 +422,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go index 1893e2fe884044..38c53ec51bb3e6 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_arm64.go @@ -325,4 +325,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go index 16a4017da0ab2f..31d2e71a18e17f 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_loong64.go @@ -321,4 +321,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go index 7e567f1efff21d..f4184a336b0e02 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips.go @@ -442,4 +442,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 4460 SYS_LSM_LIST_MODULES = 4461 SYS_MSEAL = 4462 + SYS_SETXATTRAT = 4463 + SYS_GETXATTRAT = 4464 + SYS_LISTXATTRAT = 4465 + SYS_REMOVEXATTRAT = 4466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go index 38ae55e5ef8564..05b9962278f276 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64.go @@ -372,4 +372,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 5460 SYS_LSM_LIST_MODULES = 5461 SYS_MSEAL = 5462 + SYS_SETXATTRAT = 5463 + SYS_GETXATTRAT = 5464 + SYS_LISTXATTRAT = 5465 + SYS_REMOVEXATTRAT = 5466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go index 55e92e60a82abe..43a256e9e67585 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mips64le.go @@ -372,4 +372,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 5460 SYS_LSM_LIST_MODULES = 5461 SYS_MSEAL = 5462 + SYS_SETXATTRAT = 5463 + SYS_GETXATTRAT = 5464 + SYS_LISTXATTRAT = 5465 + SYS_REMOVEXATTRAT = 5466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go index 60658d6a021f66..eea5ddfc220774 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_mipsle.go @@ -442,4 +442,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 4460 SYS_LSM_LIST_MODULES = 4461 SYS_MSEAL = 4462 + SYS_SETXATTRAT = 4463 + SYS_GETXATTRAT = 4464 + SYS_LISTXATTRAT = 4465 + SYS_REMOVEXATTRAT = 4466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go index e203e8a7ed4b2c..0d777bfbb1408e 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc.go @@ -449,4 +449,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go index 5944b97d54604e..b44636502561e6 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64.go @@ -421,4 +421,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go index c66d416dad1ccb..0c7d21c1881653 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_ppc64le.go @@ -421,4 +421,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go index a5459e766f59db..8405391698787a 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_riscv64.go @@ -326,4 +326,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go index 01d86825bb9264..fcf1b790d6cfd3 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_s390x.go @@ -387,4 +387,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go index 7b703e77cda845..52d15b5f9d4597 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/zsysnum_linux_sparc64.go @@ -400,4 +400,8 @@ const ( SYS_LSM_SET_SELF_ATTR = 460 SYS_LSM_LIST_MODULES = 461 SYS_MSEAL = 462 + SYS_SETXATTRAT = 463 + SYS_GETXATTRAT = 464 + SYS_LISTXATTRAT = 465 + SYS_REMOVEXATTRAT = 466 ) diff --git a/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go b/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go index 5537148dcbb3de..a46abe64720547 100644 --- a/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/src/cmd/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -4747,7 +4747,7 @@ const ( NL80211_ATTR_MAC_HINT = 0xc8 NL80211_ATTR_MAC_MASK = 0xd7 NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca - NL80211_ATTR_MAX = 0x14c + NL80211_ATTR_MAX = 0x14d NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4 NL80211_ATTR_MAX_CSA_COUNTERS = 0xce NL80211_ATTR_MAX_MATCH_SETS = 0x85 @@ -5519,7 +5519,7 @@ const ( NL80211_MNTR_FLAG_CONTROL = 0x3 NL80211_MNTR_FLAG_COOK_FRAMES = 0x5 NL80211_MNTR_FLAG_FCSFAIL = 0x1 - NL80211_MNTR_FLAG_MAX = 0x6 + NL80211_MNTR_FLAG_MAX = 0x7 NL80211_MNTR_FLAG_OTHER_BSS = 0x4 NL80211_MNTR_FLAG_PLCPFAIL = 0x2 NL80211_MPATH_FLAG_ACTIVE = 0x1 @@ -6174,3 +6174,5 @@ type SockDiagReq struct { Family uint8 Protocol uint8 } + +const RTM_NEWNVLAN = 0x70 diff --git a/src/cmd/vendor/golang.org/x/sys/windows/dll_windows.go b/src/cmd/vendor/golang.org/x/sys/windows/dll_windows.go index 4e613cf6335cea..3ca814f54d44eb 100644 --- a/src/cmd/vendor/golang.org/x/sys/windows/dll_windows.go +++ b/src/cmd/vendor/golang.org/x/sys/windows/dll_windows.go @@ -43,8 +43,8 @@ type DLL struct { // LoadDLL loads DLL file into memory. // // Warning: using LoadDLL without an absolute path name is subject to -// DLL preloading attacks. To safely load a system DLL, use LazyDLL -// with System set to true, or use LoadLibraryEx directly. +// DLL preloading attacks. To safely load a system DLL, use [NewLazySystemDLL], +// or use [LoadLibraryEx] directly. func LoadDLL(name string) (dll *DLL, err error) { namep, err := UTF16PtrFromString(name) if err != nil { @@ -271,6 +271,9 @@ func (d *LazyDLL) NewProc(name string) *LazyProc { } // NewLazyDLL creates new LazyDLL associated with DLL file. +// +// Warning: using NewLazyDLL without an absolute path name is subject to +// DLL preloading attacks. To safely load a system DLL, use [NewLazySystemDLL]. func NewLazyDLL(name string) *LazyDLL { return &LazyDLL{Name: name} } @@ -410,7 +413,3 @@ func loadLibraryEx(name string, system bool) (*DLL, error) { } return &DLL{Name: name, Handle: h}, nil } - -type errString string - -func (s errString) Error() string { return string(s) } diff --git a/src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go b/src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go index 612f7563a74c9f..f43d2cd455b638 100644 --- a/src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go +++ b/src/cmd/vendor/golang.org/x/telemetry/internal/crashmonitor/monitor.go @@ -170,6 +170,35 @@ func telemetryCounterName(crash []byte) (string, error) { // there is no possibility of strings from the crash report (which may // contain PII) leaking into the telemetry system. func parseStackPCs(crash string) ([]uintptr, error) { + // getSymbol parses the symbol name out of a line of the form: + // SYMBOL(ARGS) + // + // Note: SYMBOL may contain parens "pkg.(*T).method". However, type + // parameters are always replaced with ..., so they cannot introduce + // more parens. e.g., "pkg.(*T[...]).method". + // + // ARGS can contain parens. We want the first paren that is not + // immediately preceded by a ".". + // + // TODO(prattmic): This is mildly complicated and is only used to find + // runtime.sigpanic, so perhaps simplify this by checking explicitly + // for sigpanic. + getSymbol := func(line string) (string, error) { + var prev rune + for i, c := range line { + if line[i] != '(' { + prev = c + continue + } + if prev == '.' { + prev = c + continue + } + return line[:i], nil + } + return "", fmt.Errorf("no symbol for stack frame: %s", line) + } + // getPC parses the PC out of a line of the form: // \tFILE:LINE +0xRELPC sp=... fp=... pc=... getPC := func(line string) (uint64, error) { @@ -186,6 +215,9 @@ func parseStackPCs(crash string) ([]uintptr, error) { childSentinel = sentinel() on = false // are we in the first running goroutine? lines = strings.Split(crash, "\n") + symLine = true // within a goroutine, every other line is a symbol or file/line/pc location, starting with symbol. + currSymbol string + prevSymbol string // symbol of the most recent previous frame with a PC. ) for i := 0; i < len(lines); i++ { line := lines[i] @@ -228,21 +260,76 @@ func parseStackPCs(crash string) ([]uintptr, error) { // Note: SYMBOL may contain parens "pkg.(*T).method" // The RELPC is sometimes missing. - // Skip the symbol(args) line. - i++ - if i == len(lines) { - break - } - line = lines[i] + if symLine { + var err error + currSymbol, err = getSymbol(line) + if err != nil { + return nil, fmt.Errorf("error extracting symbol: %v", err) + } - // Parse the PC, and correct for the parent and child's - // different mappings of the text section. - pc, err := getPC(line) - if err != nil { - // Inlined frame, perhaps; skip it. - continue + symLine = false // Next line is FILE:LINE. + } else { + // Parse the PC, and correct for the parent and child's + // different mappings of the text section. + pc, err := getPC(line) + if err != nil { + // Inlined frame, perhaps; skip it. + + // Done with this frame. Next line is a new frame. + // + // Don't update prevSymbol; we only want to + // track frames with a PC. + currSymbol = "" + symLine = true + continue + } + + pc = pc-parentSentinel+childSentinel + + // If the previous frame was sigpanic, then this frame + // was a trap (e.g., SIGSEGV). + // + // Typically all middle frames are calls, and report + // the "return PC". That is, the instruction following + // the CALL where the callee will eventually return to. + // + // runtime.CallersFrames is aware of this property and + // will decrement each PC by 1 to "back up" to the + // location of the CALL, which is the actual line + // number the user expects. + // + // This does not work for traps, as a trap is not a + // call, so the reported PC is not the return PC, but + // the actual PC of the trap. + // + // runtime.Callers is aware of this and will + // intentionally increment trap PCs in order to correct + // for the decrement performed by + // runtime.CallersFrames. See runtime.tracebackPCs and + // runtume.(*unwinder).symPC. + // + // We must emulate the same behavior, otherwise we will + // report the location of the instruction immediately + // prior to the trap, which may be on a different line, + // or even a different inlined functions. + // + // TODO(prattmic): The runtime applies the same trap + // behavior for other "injected calls", see injectCall + // in runtime.(*unwinder).next. Do we want to handle + // those as well? I don't believe we'd ever see + // runtime.asyncPreempt or runtime.debugCallV2 in a + // typical crash. + if prevSymbol == "runtime.sigpanic" { + pc++ + } + + pcs = append(pcs, uintptr(pc)) + + // Done with this frame. Next line is a new frame. + prevSymbol = currSymbol + currSymbol = "" + symLine = true } - pcs = append(pcs, uintptr(pc-parentSentinel+childSentinel)) } return pcs, nil } diff --git a/src/cmd/vendor/golang.org/x/tools/cmd/bisect/main.go b/src/cmd/vendor/golang.org/x/tools/cmd/bisect/main.go index 6a3745c0582ae2..a152fbd37c7ef5 100644 --- a/src/cmd/vendor/golang.org/x/tools/cmd/bisect/main.go +++ b/src/cmd/vendor/golang.org/x/tools/cmd/bisect/main.go @@ -262,7 +262,7 @@ type Bisect struct { // each pattern starts with a !. Disable bool - // SkipDigits is the number of hex digits to use in skip messages. + // SkipHexDigits is the number of hex digits to use in skip messages. // If the set of available changes is the same in each run, as it should be, // then this doesn't matter: we'll only exclude suffixes that uniquely identify // a given change. But for some programs, especially bisecting runtime diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go index d384aa89b8ebde..3a73084a53c2e6 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/analysis.go @@ -156,10 +156,17 @@ type Pass struct { // AllPackageFacts returns a new slice containing all package // facts of the analysis's FactTypes in unspecified order. + // See comments for AllObjectFacts. AllPackageFacts func() []PackageFact // AllObjectFacts returns a new slice containing all object // facts of the analysis's FactTypes in unspecified order. + // + // The result includes all facts exported by packages + // whose symbols are referenced by the current package + // (by qualified identifiers or field/method selections). + // And it includes all facts exported from the current + // package by the current analysis pass. AllObjectFacts func() []ObjectFact /* Further fields may be added in future. */ diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/diagnostic.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/diagnostic.go index ee083a2d686723..f6118bec647620 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/diagnostic.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/diagnostic.go @@ -65,7 +65,9 @@ type RelatedInformation struct { // user can choose to apply to their code. Usually the SuggestedFix is // meant to fix the issue flagged by the diagnostic. // -// The TextEdits must not overlap, nor contain edits for other packages. +// The TextEdits must not overlap, nor contain edits for other +// packages. Edits need not be totally ordered, but the order +// determines how insertions at the same point will be applied. type SuggestedFix struct { // A verb phrase describing the fix, to be shown to // a user trying to decide whether to accept it. diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go index 1282e70d41f703..c2445575cff8cf 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/internal/analysisflags/flags.go @@ -250,21 +250,12 @@ const ( setFalse ) -func triStateFlag(name string, value triState, usage string) *triState { - flag.Var(&value, name, usage) - return &value -} - // triState implements flag.Value, flag.Getter, and flag.boolFlag. // They work like boolean flags: we can say vet -printf as well as vet -printf=true func (ts *triState) Get() interface{} { return *ts == setTrue } -func (ts triState) isTrue() bool { - return ts == setTrue -} - func (ts *triState) Set(value string) error { b, err := strconv.ParseBool(value) if err != nil { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go index b622dfdf3a06aa..a47ecbae731a51 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go @@ -542,8 +542,8 @@ func appendComponentsRecursive(arch *asmArch, t types.Type, cc []component, suff elem := tu.Elem() // Calculate offset of each element array. fields := []*types.Var{ - types.NewVar(token.NoPos, nil, "fake0", elem), - types.NewVar(token.NoPos, nil, "fake1", elem), + types.NewField(token.NoPos, nil, "fake0", elem, false), + types.NewField(token.NoPos, nil, "fake1", elem, false), } offsets := arch.sizes.Offsetsof(fields) elemoff := int(offsets[1]) diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go index 0d95fefcb5a286..1413ee13d293e0 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/assign/assign.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -32,7 +33,7 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ @@ -57,15 +58,17 @@ func run(pass *analysis.Pass) (interface{}, error) { if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { continue // short-circuit the heavy-weight gofmt check } - le := analysisutil.Format(pass.Fset, lhs) - re := analysisutil.Format(pass.Fset, rhs) + le := analysisinternal.Format(pass.Fset, lhs) + re := analysisinternal.Format(pass.Fset, rhs) if le == re { pass.Report(analysis.Diagnostic{ Pos: stmt.Pos(), Message: fmt.Sprintf("self-assignment of %s to %s", re, le), - SuggestedFixes: []analysis.SuggestedFix{ - {Message: "Remove", TextEdits: []analysis.TextEdit{ - {Pos: stmt.Pos(), End: stmt.End(), NewText: []byte{}}, - }}, + SuggestedFixes: []analysis.SuggestedFix{{ + Message: "Remove self-assignment", + TextEdits: []analysis.TextEdit{{ + Pos: stmt.Pos(), + End: stmt.End(), + }}}, }, }) } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go index 931f9ca7540f65..82d5439ce571e9 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go @@ -14,6 +14,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -28,8 +29,8 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { - if !analysisutil.Imports(pass.Pkg, "sync/atomic") { +func run(pass *analysis.Pass) (any, error) { + if !analysisinternal.Imports(pass.Pkg, "sync/atomic") { return nil, nil // doesn't directly import sync/atomic } @@ -52,8 +53,8 @@ func run(pass *analysis.Pass) (interface{}, error) { if !ok { continue } - fn := typeutil.StaticCallee(pass.TypesInfo, call) - if analysisutil.IsFunctionNamed(fn, "sync/atomic", "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr") { + obj := typeutil.Callee(pass.TypesInfo, call) + if analysisinternal.IsFunctionNamed(obj, "sync/atomic", "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr") { checkAtomicAddAssignment(pass, n.Lhs[i], call) } } @@ -71,7 +72,7 @@ func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.Call arg := call.Args[0] broken := false - gofmt := func(e ast.Expr) string { return analysisutil.Format(pass.Fset, e) } + gofmt := func(e ast.Expr) string { return analysisinternal.Format(pass.Fset, e) } if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { broken = gofmt(left) == gofmt(uarg.X) diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go index 8cec6e8224a5dc..e1cf9f9b7ade10 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/bools/bools.go @@ -15,6 +15,7 @@ import ( "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysisinternal" ) const Doc = "check for common mistakes involving boolean operators" @@ -27,7 +28,7 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ @@ -103,7 +104,7 @@ func (op boolOp) commutativeSets(info *types.Info, e *ast.BinaryExpr, seen map[* func (op boolOp) checkRedundant(pass *analysis.Pass, exprs []ast.Expr) { seen := make(map[string]bool) for _, e := range exprs { - efmt := analysisutil.Format(pass.Fset, e) + efmt := analysisinternal.Format(pass.Fset, e) if seen[efmt] { pass.ReportRangef(e, "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt) } else { @@ -149,8 +150,8 @@ func (op boolOp) checkSuspect(pass *analysis.Pass, exprs []ast.Expr) { } // e is of the form 'x != c' or 'x == c'. - xfmt := analysisutil.Format(pass.Fset, x) - efmt := analysisutil.Format(pass.Fset, e) + xfmt := analysisinternal.Format(pass.Fset, x) + efmt := analysisinternal.Format(pass.Fset, e) if prev, found := seen[xfmt]; found { // checkRedundant handles the case in which efmt == prev. if efmt != prev { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go index 613583a1a64909..4f3bb035d65783 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/cgocall/cgocall.go @@ -18,7 +18,7 @@ import ( "strconv" "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/internal/analysisutil" + "golang.org/x/tools/internal/analysisinternal" ) const debug = false @@ -40,8 +40,8 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { - if !analysisutil.Imports(pass.Pkg, "runtime/cgo") { +func run(pass *analysis.Pass) (any, error) { + if !analysisinternal.Imports(pass.Pkg, "runtime/cgo") { return nil, nil // doesn't use cgo } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go index 03496cb3037d14..a9f02ac62e6405 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go @@ -15,8 +15,8 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/versions" ) @@ -86,7 +86,7 @@ func checkCopyLocksAssign(pass *analysis.Pass, assign *ast.AssignStmt, goversion lhs := assign.Lhs for i, x := range assign.Rhs { if path := lockPathRhs(pass, x); path != nil { - pass.ReportRangef(x, "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, assign.Lhs[i]), path) + pass.ReportRangef(x, "assignment copies lock value to %v: %v", analysisinternal.Format(pass.Fset, assign.Lhs[i]), path) lhs = nil // An lhs has been reported. We prefer the assignment warning and do not report twice. } } @@ -100,7 +100,7 @@ func checkCopyLocksAssign(pass *analysis.Pass, assign *ast.AssignStmt, goversion if id, ok := l.(*ast.Ident); ok && id.Name != "_" { if obj := pass.TypesInfo.Defs[id]; obj != nil && obj.Type() != nil { if path := lockPath(pass.Pkg, obj.Type(), nil); path != nil { - pass.ReportRangef(l, "for loop iteration copies lock value to %v: %v", analysisutil.Format(pass.Fset, l), path) + pass.ReportRangef(l, "for loop iteration copies lock value to %v: %v", analysisinternal.Format(pass.Fset, l), path) } } } @@ -132,7 +132,7 @@ func checkCopyLocksCompositeLit(pass *analysis.Pass, cl *ast.CompositeLit) { x = node.Value } if path := lockPathRhs(pass, x); path != nil { - pass.ReportRangef(x, "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path) + pass.ReportRangef(x, "literal copies lock value from %v: %v", analysisinternal.Format(pass.Fset, x), path) } } } @@ -163,7 +163,7 @@ func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) { } for _, x := range ce.Args { if path := lockPathRhs(pass, x); path != nil { - pass.ReportRangef(x, "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, ce.Fun), path) + pass.ReportRangef(x, "call of %s copies lock value: %v", analysisinternal.Format(pass.Fset, ce.Fun), path) } } } @@ -230,7 +230,7 @@ func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) { return } if path := lockPath(pass.Pkg, typ, nil); path != nil { - pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path) + pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisinternal.Format(pass.Fset, e), path) } } @@ -350,7 +350,7 @@ func lockPath(tpkg *types.Package, typ types.Type, seen map[types.Type]bool) typ // In go1.10, sync.noCopy did not implement Locker. // (The Unlock method was added only in CL 121876.) // TODO(adonovan): remove workaround when we drop go1.10. - if analysisutil.IsNamedType(typ, "sync", "noCopy") { + if analysisinternal.IsTypeNamed(typ, "sync", "noCopy") { return []string{typ.String()} } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/defers/defers.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/defers/defers.go index 5e8e80a6a77cd2..e11957f2d099d1 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/defers/defers.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/defers/defers.go @@ -13,6 +13,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -27,15 +28,15 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { - if !analysisutil.Imports(pass.Pkg, "time") { +func run(pass *analysis.Pass) (any, error) { + if !analysisinternal.Imports(pass.Pkg, "time") { return nil, nil } checkDeferCall := func(node ast.Node) bool { switch v := node.(type) { case *ast.CallExpr: - if analysisutil.IsFunctionNamed(typeutil.StaticCallee(pass.TypesInfo, v), "time", "Since") { + if analysisinternal.IsFunctionNamed(typeutil.Callee(pass.TypesInfo, v), "time", "Since") { pass.Reportf(v.Pos(), "call to time.Since is not deferred") } case *ast.FuncLit: diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go index 7f62ad4c825d6a..b8d29d019db0b0 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/errorsas/errorsas.go @@ -13,9 +13,9 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" ) const Doc = `report passing non-pointer or non-error values to errors.As @@ -31,7 +31,7 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { switch pass.Pkg.Path() { case "errors", "errors_test": // These packages know how to use their own APIs. @@ -39,7 +39,7 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, nil } - if !analysisutil.Imports(pass.Pkg, "errors") { + if !analysisinternal.Imports(pass.Pkg, "errors") { return nil, nil // doesn't directly import errors } @@ -50,8 +50,8 @@ func run(pass *analysis.Pass) (interface{}, error) { } inspect.Preorder(nodeFilter, func(n ast.Node) { call := n.(*ast.CallExpr) - fn := typeutil.StaticCallee(pass.TypesInfo, call) - if !analysisutil.IsFunctionNamed(fn, "errors", "As") { + obj := typeutil.Callee(pass.TypesInfo, call) + if !analysisinternal.IsFunctionNamed(obj, "errors", "As") { return } if len(call.Args) < 2 { diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go index 6eff3a20feae11..8012de99daaedb 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/framepointer/framepointer.go @@ -10,6 +10,7 @@ import ( "go/build" "regexp" "strings" + "unicode" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" @@ -24,15 +25,97 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -var ( - re = regexp.MustCompile - asmWriteBP = re(`,\s*BP$`) // TODO: can have false positive, e.g. for TESTQ BP,BP. Seems unlikely. - asmMentionBP = re(`\bBP\b`) - asmControlFlow = re(`^(J|RET)`) -) +// Per-architecture checks for instructions. +// Assume comments, leading and trailing spaces are removed. +type arch struct { + isFPWrite func(string) bool + isFPRead func(string) bool + isBranch func(string) bool +} + +var re = regexp.MustCompile + +func hasAnyPrefix(s string, prefixes ...string) bool { + for _, p := range prefixes { + if strings.HasPrefix(s, p) { + return true + } + } + return false +} + +var arches = map[string]arch{ + "amd64": { + isFPWrite: re(`,\s*BP$`).MatchString, // TODO: can have false positive, e.g. for TESTQ BP,BP. Seems unlikely. + isFPRead: re(`\bBP\b`).MatchString, + isBranch: func(s string) bool { + return hasAnyPrefix(s, "J", "RET") + }, + }, + "arm64": { + isFPWrite: func(s string) bool { + if i := strings.LastIndex(s, ","); i > 0 && strings.HasSuffix(s[i:], "R29") { + return true + } + if hasAnyPrefix(s, "LDP", "LDAXP", "LDXP", "CASP") { + // Instructions which write to a pair of registers, e.g. + // LDP 8(R0), (R26, R29) + // CASPD (R2, R3), (R2), (R26, R29) + lp := strings.LastIndex(s, "(") + rp := strings.LastIndex(s, ")") + if lp > -1 && lp < rp { + return strings.Contains(s[lp:rp], ",") && strings.Contains(s[lp:rp], "R29") + } + } + return false + }, + isFPRead: re(`\bR29\b`).MatchString, + isBranch: func(s string) bool { + // Get just the instruction + if i := strings.IndexFunc(s, unicode.IsSpace); i > 0 { + s = s[:i] + } + return arm64Branch[s] + }, + }, +} + +// arm64 has many control flow instructions. +// ^(B|RET) isn't sufficient or correct (e.g. BIC, BFI aren't control flow.) +// It's easier to explicitly enumerate them in a map than to write a regex. +// Borrowed from Go tree, cmd/asm/internal/arch/arm64.go +var arm64Branch = map[string]bool{ + "B": true, + "BL": true, + "BEQ": true, + "BNE": true, + "BCS": true, + "BHS": true, + "BCC": true, + "BLO": true, + "BMI": true, + "BPL": true, + "BVS": true, + "BVC": true, + "BHI": true, + "BLS": true, + "BGE": true, + "BLT": true, + "BGT": true, + "BLE": true, + "CBZ": true, + "CBZW": true, + "CBNZ": true, + "CBNZW": true, + "JMP": true, + "TBNZ": true, + "TBZ": true, + "RET": true, +} func run(pass *analysis.Pass) (interface{}, error) { - if build.Default.GOARCH != "amd64" { // TODO: arm64 also? + arch, ok := arches[build.Default.GOARCH] + if !ok { return nil, nil } if build.Default.GOOS != "linux" && build.Default.GOOS != "darwin" { @@ -63,6 +146,9 @@ func run(pass *analysis.Pass) (interface{}, error) { line = line[:i] } line = strings.TrimSpace(line) + if line == "" { + continue + } // We start checking code at a TEXT line for a frameless function. if strings.HasPrefix(line, "TEXT") && strings.Contains(line, "(SB)") && strings.Contains(line, "$0") { @@ -73,16 +159,12 @@ func run(pass *analysis.Pass) (interface{}, error) { continue } - if asmWriteBP.MatchString(line) { // clobber of BP, function is not OK + if arch.isFPWrite(line) { pass.Reportf(analysisutil.LineStart(tf, lineno), "frame pointer is clobbered before saving") active = false continue } - if asmMentionBP.MatchString(line) { // any other use of BP might be a read, so function is OK - active = false - continue - } - if asmControlFlow.MatchString(line) { // give up after any branch instruction + if arch.isFPRead(line) || arch.isBranch(line) { active = false continue } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go index 91ebe29de117a3..e9acd96547e1a7 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/httpresponse/httpresponse.go @@ -12,8 +12,8 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/typesinternal" ) @@ -41,12 +41,12 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) // Fast path: if the package doesn't import net/http, // skip the traversal. - if !analysisutil.Imports(pass.Pkg, "net/http") { + if !analysisinternal.Imports(pass.Pkg, "net/http") { return nil, nil } @@ -118,7 +118,7 @@ func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool { return false // the function called does not return two values. } isPtr, named := typesinternal.ReceiverNamed(res.At(0)) - if !isPtr || named == nil || !analysisutil.IsNamedType(named, "net/http", "Response") { + if !isPtr || named == nil || !analysisinternal.IsTypeNamed(named, "net/http", "Response") { return false // the first return type is not *http.Response. } @@ -133,11 +133,11 @@ func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool { return ok && id.Name == "http" // function in net/http package. } - if analysisutil.IsNamedType(typ, "net/http", "Client") { + if analysisinternal.IsTypeNamed(typ, "net/http", "Client") { return true // method on http.Client. } ptr, ok := types.Unalias(typ).(*types.Pointer) - return ok && analysisutil.IsNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client. + return ok && analysisinternal.IsTypeNamed(ptr.Elem(), "net/http", "Client") // method on *http.Client. } // restOfBlock, given a traversal stack, finds the innermost containing diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go index a4fa8d31c4ed5b..d3df898d3011cd 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go @@ -7,9 +7,7 @@ package analysisutil import ( - "bytes" "go/ast" - "go/printer" "go/token" "go/types" "os" @@ -18,13 +16,6 @@ import ( "golang.org/x/tools/internal/analysisinternal" ) -// Format returns a string representation of the expression. -func Format(fset *token.FileSet, x ast.Expr) string { - var b bytes.Buffer - printer.Fprint(&b, fset, x) - return b.String() -} - // HasSideEffects reports whether evaluation of e has side effects. func HasSideEffects(info *types.Info, e ast.Expr) bool { safe := true @@ -105,57 +96,4 @@ func LineStart(f *token.File, line int) token.Pos { } } -// Imports returns true if path is imported by pkg. -func Imports(pkg *types.Package, path string) bool { - for _, imp := range pkg.Imports() { - if imp.Path() == path { - return true - } - } - return false -} - -// IsNamedType reports whether t is the named type with the given package path -// and one of the given names. -// This function avoids allocating the concatenation of "pkg.Name", -// which is important for the performance of syntax matching. -func IsNamedType(t types.Type, pkgPath string, names ...string) bool { - n, ok := types.Unalias(t).(*types.Named) - if !ok { - return false - } - obj := n.Obj() - if obj == nil || obj.Pkg() == nil || obj.Pkg().Path() != pkgPath { - return false - } - name := obj.Name() - for _, n := range names { - if name == n { - return true - } - } - return false -} - -// IsFunctionNamed reports whether f is a top-level function defined in the -// given package and has one of the given names. -// It returns false if f is nil or a method. -func IsFunctionNamed(f *types.Func, pkgPath string, names ...string) bool { - if f == nil { - return false - } - if f.Pkg() == nil || f.Pkg().Path() != pkgPath { - return false - } - if f.Type().(*types.Signature).Recv() != nil { - return false - } - for _, n := range names { - if f.Name() == n { - return true - } - } - return false -} - var MustExtractDoc = analysisinternal.MustExtractDoc diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go index fe05eda44e49d3..d3181242153764 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/loopclosure/loopclosure.go @@ -14,6 +14,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/typesinternal" "golang.org/x/tools/internal/versions" ) @@ -368,5 +369,5 @@ func isMethodCall(info *types.Info, expr ast.Expr, pkgPath, typeName, method str // Check that the receiver is a . or // *.. _, named := typesinternal.ReceiverNamed(recv) - return analysisutil.IsNamedType(named, pkgPath, typeName) + return analysisinternal.IsTypeNamed(named, pkgPath, typeName) } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go index 26fdc1206f8055..f8a661aa5db059 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/lostcancel/lostcancel.go @@ -16,6 +16,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/cfg" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -48,7 +49,7 @@ var contextPackage = "context" // checkLostCancel analyzes a single named or literal function. func run(pass *analysis.Pass) (interface{}, error) { // Fast path: bypass check if file doesn't use context.WithCancel. - if !analysisutil.Imports(pass.Pkg, contextPackage) { + if !analysisinternal.Imports(pass.Pkg, contextPackage) { return nil, nil } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go index 171ad2013722c4..81600a283aa21c 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go @@ -5,7 +5,6 @@ package printf import ( - "bytes" _ "embed" "fmt" "go/ast" @@ -15,16 +14,17 @@ import ( "reflect" "regexp" "sort" - "strconv" "strings" - "unicode/utf8" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" + "golang.org/x/tools/internal/fmtstr" "golang.org/x/tools/internal/typeparams" + "golang.org/x/tools/internal/versions" ) func init() { @@ -108,12 +108,12 @@ func (f *isWrapper) String() string { } } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { res := &Result{ funcs: make(map[*types.Func]Kind), } findPrintfLike(pass, res) - checkCall(pass) + checkCalls(pass) return res, nil } @@ -182,7 +182,7 @@ func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper { } // findPrintfLike scans the entire package to find printf-like functions. -func findPrintfLike(pass *analysis.Pass, res *Result) (interface{}, error) { +func findPrintfLike(pass *analysis.Pass, res *Result) (any, error) { // Gather potential wrappers and call graph between them. byObj := make(map[*types.Func]*printfWrapper) var wrappers []*printfWrapper @@ -409,20 +409,29 @@ func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) { return "", false } -// checkCall triggers the print-specific checks if the call invokes a print function. -func checkCall(pass *analysis.Pass) { +// checkCalls triggers the print-specific checks for calls that invoke a print +// function. +func checkCalls(pass *analysis.Pass) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ + (*ast.File)(nil), (*ast.CallExpr)(nil), } + + var fileVersion string // for selectively suppressing checks; "" if unknown. inspect.Preorder(nodeFilter, func(n ast.Node) { - call := n.(*ast.CallExpr) - fn, kind := printfNameAndKind(pass, call) - switch kind { - case KindPrintf, KindErrorf: - checkPrintf(pass, kind, call, fn) - case KindPrint: - checkPrint(pass, call, fn) + switch n := n.(type) { + case *ast.File: + fileVersion = versions.Lang(versions.FileVersion(pass.TypesInfo, n)) + + case *ast.CallExpr: + fn, kind := printfNameAndKind(pass, n) + switch kind { + case KindPrintf, KindErrorf: + checkPrintf(pass, fileVersion, kind, n, fn.FullName()) + case KindPrint: + checkPrint(pass, n, fn.FullName()) + } } }) } @@ -480,30 +489,12 @@ func isFormatter(typ types.Type) bool { sig := fn.Type().(*types.Signature) return sig.Params().Len() == 2 && sig.Results().Len() == 0 && - analysisutil.IsNamedType(sig.Params().At(0).Type(), "fmt", "State") && + analysisinternal.IsTypeNamed(sig.Params().At(0).Type(), "fmt", "State") && types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune]) } -// formatState holds the parsed representation of a printf directive such as "%3.*[4]d". -// It is constructed by parsePrintfVerb. -type formatState struct { - verb rune // the format verb: 'd' for "%d" - format string // the full format directive from % through verb, "%.3d". - name string // Printf, Sprintf etc. - flags []byte // the list of # + etc. - argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call - firstArg int // Index of first argument after the format in the Printf call. - // Used only during parse. - pass *analysis.Pass - call *ast.CallExpr - argNum int // Which argument we're expecting to format now. - hasIndex bool // Whether the argument is indexed. - indexPending bool // Whether we have an indexed argument that has not resolved. - nbytes int // number of bytes of the format string consumed. -} - // checkPrintf checks a call to a formatted print routine such as Printf. -func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) { +func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.CallExpr, name string) { idx := formatStringIndex(pass, call) if idx < 0 || idx >= len(call.Args) { return @@ -517,12 +508,22 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F // non-constant format string and no arguments: // if msg contains "%", misformatting occurs. // Report the problem and suggest a fix: fmt.Printf("%s", msg). - if !suppressNonconstants && idx == len(call.Args)-1 { + // + // However, as described in golang/go#71485, this analysis can produce a + // significant number of diagnostics in existing code, and the bugs it + // finds are sometimes unlikely or inconsequential, and may not be worth + // fixing for some users. Gating on language version allows us to avoid + // breaking existing tests and CI scripts. + if !suppressNonconstants && + idx == len(call.Args)-1 && + fileVersion != "" && // fail open + versions.AtLeast(fileVersion, "go1.24") { + pass.Report(analysis.Diagnostic{ Pos: formatArg.Pos(), End: formatArg.End(), Message: fmt.Sprintf("non-constant format string in call to %s", - fn.FullName()), + name), SuggestedFixes: []analysis.SuggestedFix{{ Message: `Insert "%s" format string`, TextEdits: []analysis.TextEdit{{ @@ -539,49 +540,46 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F firstArg := idx + 1 // Arguments are immediately after format string. if !strings.Contains(format, "%") { if len(call.Args) > firstArg { - pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fn.FullName()) + pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", name) } return } - // Hard part: check formats against args. - argNum := firstArg - maxArgNum := firstArg + + // Pass the string constant value so + // fmt.Sprintf("%"+("s"), "hi", 3) can be reported as + // "fmt.Sprintf call needs 1 arg but has 2 args". + operations, err := fmtstr.Parse(format, idx) + if err != nil { + // All error messages are in predicate form ("call has a problem") + // so that they may be affixed into a subject ("log.Printf "). + pass.ReportRangef(call.Args[idx], "%s %s", name, err) + return + } + + // index of the highest used index. + maxArgIndex := firstArg - 1 anyIndex := false - for i, w := 0, 0; i < len(format); i += w { - w = 1 - if format[i] != '%' { - continue - } - state := parsePrintfVerb(pass, call, fn.FullName(), format[i:], firstArg, argNum) - if state == nil { - return + // Check formats against args. + for _, operation := range operations { + if operation.Prec.Index != -1 || + operation.Width.Index != -1 || + operation.Verb.Index != -1 { + anyIndex = true } - w = len(state.format) - if !okPrintfArg(pass, call, state) { // One error per format is enough. + if !okPrintfArg(pass, call, &maxArgIndex, firstArg, name, operation) { + // One error per format is enough. return } - if state.hasIndex { - anyIndex = true - } - if state.verb == 'w' { + if operation.Verb.Verb == 'w' { switch kind { case KindNone, KindPrint, KindPrintf: - pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name) + pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", name) return } } - if len(state.argNums) > 0 { - // Continue with the next sequential argument. - argNum = state.argNums[len(state.argNums)-1] + 1 - } - for _, n := range state.argNums { - if n >= maxArgNum { - maxArgNum = n + 1 - } - } } // Dotdotdot is hard. - if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 { + if call.Ellipsis.IsValid() && maxArgIndex >= len(call.Args)-2 { return } // If any formats are indexed, extra arguments are ignored. @@ -589,145 +587,11 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F return } // There should be no leftover arguments. - if maxArgNum != len(call.Args) { - expect := maxArgNum - firstArg + if maxArgIndex+1 < len(call.Args) { + expect := maxArgIndex + 1 - firstArg numArgs := len(call.Args) - firstArg - pass.ReportRangef(call, "%s call needs %v but has %v", fn.FullName(), count(expect, "arg"), count(numArgs, "arg")) - } -} - -// parseFlags accepts any printf flags. -func (s *formatState) parseFlags() { - for s.nbytes < len(s.format) { - switch c := s.format[s.nbytes]; c { - case '#', '0', '+', '-', ' ': - s.flags = append(s.flags, c) - s.nbytes++ - default: - return - } - } -} - -// scanNum advances through a decimal number if present. -func (s *formatState) scanNum() { - for ; s.nbytes < len(s.format); s.nbytes++ { - c := s.format[s.nbytes] - if c < '0' || '9' < c { - return - } - } -} - -// parseIndex scans an index expression. It returns false if there is a syntax error. -func (s *formatState) parseIndex() bool { - if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' { - return true - } - // Argument index present. - s.nbytes++ // skip '[' - start := s.nbytes - s.scanNum() - ok := true - if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { - ok = false // syntax error is either missing "]" or invalid index. - s.nbytes = strings.Index(s.format[start:], "]") - if s.nbytes < 0 { - s.pass.ReportRangef(s.call, "%s format %s is missing closing ]", s.name, s.format) - return false - } - s.nbytes = s.nbytes + start + pass.ReportRangef(call, "%s call needs %v but has %v", name, count(expect, "arg"), count(numArgs, "arg")) } - arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) - if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) { - s.pass.ReportRangef(s.call, "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes]) - return false - } - s.nbytes++ // skip ']' - arg := int(arg32) - arg += s.firstArg - 1 // We want to zero-index the actual arguments. - s.argNum = arg - s.hasIndex = true - s.indexPending = true - return true -} - -// parseNum scans a width or precision (or *). It returns false if there's a bad index expression. -func (s *formatState) parseNum() bool { - if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' { - if s.indexPending { // Absorb it. - s.indexPending = false - } - s.nbytes++ - s.argNums = append(s.argNums, s.argNum) - s.argNum++ - } else { - s.scanNum() - } - return true -} - -// parsePrecision scans for a precision. It returns false if there's a bad index expression. -func (s *formatState) parsePrecision() bool { - // If there's a period, there may be a precision. - if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' { - s.flags = append(s.flags, '.') // Treat precision as a flag. - s.nbytes++ - if !s.parseIndex() { - return false - } - if !s.parseNum() { - return false - } - } - return true -} - -// parsePrintfVerb looks the formatting directive that begins the format string -// and returns a formatState that encodes what the directive wants, without looking -// at the actual arguments present in the call. The result is nil if there is an error. -func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { - state := &formatState{ - format: format, - name: name, - flags: make([]byte, 0, 5), - argNum: argNum, - argNums: make([]int, 0, 1), - nbytes: 1, // There's guaranteed to be a percent sign. - firstArg: firstArg, - pass: pass, - call: call, - } - // There may be flags. - state.parseFlags() - // There may be an index. - if !state.parseIndex() { - return nil - } - // There may be a width. - if !state.parseNum() { - return nil - } - // There may be a precision. - if !state.parsePrecision() { - return nil - } - // Now a verb, possibly prefixed by an index (which we may already have). - if !state.indexPending && !state.parseIndex() { - return nil - } - if state.nbytes == len(state.format) { - pass.ReportRangef(call.Fun, "%s format %s is missing verb at end of string", name, state.format) - return nil - } - verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) - state.verb = verb - state.nbytes += w - if verb != '%' { - state.argNums = append(state.argNums, state.argNum) - } - state.format = state.format[:state.nbytes] - return state } // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. @@ -790,79 +654,96 @@ var printVerbs = []printVerb{ {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex}, } -// okPrintfArg compares the formatState to the arguments actually present, -// reporting any discrepancies it can discern. If the final argument is ellipsissed, -// there's little it can do for that. -func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (ok bool) { +// okPrintfArg compares the operation to the arguments actually present, +// reporting any discrepancies it can discern, maxArgIndex was the index of the highest used index. +// If the final argument is ellipsissed, there's little it can do for that. +func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firstArg int, name string, operation *fmtstr.Operation) (ok bool) { + verb := operation.Verb.Verb var v printVerb found := false // Linear scan is fast enough for a small list. for _, v = range printVerbs { - if v.verb == state.verb { + if v.verb == verb { found = true break } } - // Could current arg implement fmt.Formatter? + // Could verb's arg implement fmt.Formatter? // Skip check for the %w verb, which requires an error. formatter := false - if v.typ != argError && state.argNum < len(call.Args) { - if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok { + if v.typ != argError && operation.Verb.ArgIndex < len(call.Args) { + if tv, ok := pass.TypesInfo.Types[call.Args[operation.Verb.ArgIndex]]; ok { formatter = isFormatter(tv.Type) } } if !formatter { if !found { - pass.ReportRangef(call, "%s format %s has unknown verb %c", state.name, state.format, state.verb) + pass.ReportRangef(call, "%s format %s has unknown verb %c", name, operation.Text, verb) return false } - for _, flag := range state.flags { + for _, flag := range operation.Flags { // TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11. // See issues 23598 and 23605. if flag == '0' { continue } if !strings.ContainsRune(v.flags, rune(flag)) { - pass.ReportRangef(call, "%s format %s has unrecognized flag %c", state.name, state.format, flag) + pass.ReportRangef(call, "%s format %s has unrecognized flag %c", name, operation.Text, flag) return false } } } - // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all - // but the final arg must be an integer. - trueArgs := 1 - if state.verb == '%' { - trueArgs = 0 + + var argIndexes []int + // First check for *. + if operation.Width.Dynamic != -1 { + argIndexes = append(argIndexes, operation.Width.Dynamic) + } + if operation.Prec.Dynamic != -1 { + argIndexes = append(argIndexes, operation.Prec.Dynamic) } - nargs := len(state.argNums) - for i := 0; i < nargs-trueArgs; i++ { - argNum := state.argNums[i] - if !argCanBeChecked(pass, call, i, state) { + // If len(argIndexes)>0, we have something like %.*s and all + // indexes in argIndexes must be an integer. + for _, argIndex := range argIndexes { + if !argCanBeChecked(pass, call, argIndex, firstArg, operation, name) { return } - arg := call.Args[argNum] + arg := call.Args[argIndex] if reason, ok := matchArgType(pass, argInt, arg); !ok { details := "" if reason != "" { details = " (" + reason + ")" } - pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details) + pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", name, operation.Text, analysisinternal.Format(pass.Fset, arg), details) return false } } - if state.verb == '%' || formatter { + // Collect to update maxArgNum in one loop. + if operation.Verb.ArgIndex != -1 && verb != '%' { + argIndexes = append(argIndexes, operation.Verb.ArgIndex) + } + for _, index := range argIndexes { + *maxArgIndex = max(*maxArgIndex, index) + } + + // Special case for '%', go will print "fmt.Printf("%10.2%%dhello", 4)" + // as "%4hello", discard any runes between the two '%'s, and treat the verb '%' + // as an ordinary rune, so early return to skip the type check. + if verb == '%' || formatter { return true } - argNum := state.argNums[len(state.argNums)-1] - if !argCanBeChecked(pass, call, len(state.argNums)-1, state) { + + // Now check verb's type. + verbArgIndex := operation.Verb.ArgIndex + if !argCanBeChecked(pass, call, verbArgIndex, firstArg, operation, name) { return false } - arg := call.Args[argNum] - if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' { - pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.Format(pass.Fset, arg)) + arg := call.Args[verbArgIndex] + if isFunctionValue(pass, arg) && verb != 'p' && verb != 'T' { + pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", name, operation.Text, analysisinternal.Format(pass.Fset, arg)) return false } if reason, ok := matchArgType(pass, v.typ, arg); !ok { @@ -874,12 +755,12 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o if reason != "" { details = " (" + reason + ")" } - pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details) + pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", name, operation.Text, analysisinternal.Format(pass.Fset, arg), typeString, details) return false } - if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) { + if v.typ&argString != 0 && v.verb != 'T' && !strings.Contains(operation.Flags, "#") { if methodName, ok := recursiveStringer(pass, arg); ok { - pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", state.name, state.format, analysisutil.Format(pass.Fset, arg), methodName) + pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", name, operation.Text, analysisinternal.Format(pass.Fset, arg), methodName) return false } } @@ -963,25 +844,24 @@ func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool { // argCanBeChecked reports whether the specified argument is statically present; // it may be beyond the list of arguments or in a terminal slice... argument, which // means we can't see it. -func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, formatArg int, state *formatState) bool { - argNum := state.argNums[formatArg] - if argNum <= 0 { +func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, argIndex, firstArg int, operation *fmtstr.Operation, name string) bool { + if argIndex <= 0 { // Shouldn't happen, so catch it with prejudice. - panic("negative arg num") + panic("negative argIndex") } - if argNum < len(call.Args)-1 { + if argIndex < len(call.Args)-1 { return true // Always OK. } if call.Ellipsis.IsValid() { return false // We just can't tell; there could be many more arguments. } - if argNum < len(call.Args) { + if argIndex < len(call.Args) { return true } // There are bad indexes in the format or there are fewer arguments than the format needs. // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". - arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. - pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg")) + arg := argIndex - firstArg + 1 // People think of arguments as 1-indexed. + pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", name, operation.Text, arg, count(len(call.Args)-firstArg, "arg")) return false } @@ -998,7 +878,7 @@ const ( ) // checkPrint checks a call to an unformatted print routine such as Println. -func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { +func checkPrint(pass *analysis.Pass, call *ast.CallExpr, name string) { firstArg := 0 typ := pass.TypesInfo.Types[call.Fun].Type if typ == nil { @@ -1032,7 +912,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { if sel, ok := call.Args[0].(*ast.SelectorExpr); ok { if x, ok := sel.X.(*ast.Ident); ok { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { - pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", fn.FullName(), analysisutil.Format(pass.Fset, call.Args[0])) + pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", name, analysisinternal.Format(pass.Fset, call.Args[0])) } } } @@ -1046,25 +926,25 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { if strings.Contains(s, "%") { m := printFormatRE.FindStringSubmatch(s) if m != nil { - pass.ReportRangef(call, "%s call has possible Printf formatting directive %s", fn.FullName(), m[0]) + pass.ReportRangef(call, "%s call has possible Printf formatting directive %s", name, m[0]) } } } - if strings.HasSuffix(fn.Name(), "ln") { + if strings.HasSuffix(name, "ln") { // The last item, if a string, should not have a newline. arg = args[len(args)-1] if s, ok := stringConstantExpr(pass, arg); ok { if strings.HasSuffix(s, "\n") { - pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.FullName()) + pass.ReportRangef(call, "%s arg list ends with redundant newline", name) } } } for _, arg := range args { if isFunctionValue(pass, arg) { - pass.ReportRangef(call, "%s arg %s is a func value, not called", fn.FullName(), analysisutil.Format(pass.Fset, arg)) + pass.ReportRangef(call, "%s arg %s is a func value, not called", name, analysisinternal.Format(pass.Fset, arg)) } if methodName, ok := recursiveStringer(pass, arg); ok { - pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", fn.FullName(), analysisutil.Format(pass.Fset, arg), methodName) + pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", name, analysisinternal.Format(pass.Fset, arg), methodName) } } } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go index 759ed0043ff17c..46b5f6d68c612a 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go @@ -19,8 +19,8 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/typeparams" ) @@ -123,7 +123,7 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) { } } if amt >= minSize { - ident := analysisutil.Format(pass.Fset, x) + ident := analysisinternal.Format(pass.Fset, x) qualifier := "" if len(sizes) > 1 { qualifier = "may be " diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/sigchanyzer/sigchanyzer.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/sigchanyzer/sigchanyzer.go index 5f121f720d8315..78a2fa5ea3bd34 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/sigchanyzer/sigchanyzer.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/sigchanyzer/sigchanyzer.go @@ -8,6 +8,8 @@ package sigchanyzer import ( "bytes" + "slices" + _ "embed" "go/ast" "go/format" @@ -18,6 +20,7 @@ import ( "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -32,8 +35,8 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { - if !analysisutil.Imports(pass.Pkg, "os/signal") { +func run(pass *analysis.Pass) (any, error) { + if !analysisinternal.Imports(pass.Pkg, "os/signal") { return nil, nil // doesn't directly import signal } @@ -69,7 +72,7 @@ func run(pass *analysis.Pass) (interface{}, error) { // mutating the AST. See https://golang.org/issue/46129. chanDeclCopy := &ast.CallExpr{} *chanDeclCopy = *chanDecl - chanDeclCopy.Args = append([]ast.Expr(nil), chanDecl.Args...) + chanDeclCopy.Args = slices.Clone(chanDecl.Args) chanDeclCopy.Args = append(chanDeclCopy.Args, &ast.BasicLit{ Kind: token.INT, Value: "1", diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/slog/slog.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/slog/slog.go index 0129102a33695b..c1ac960435d41f 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/slog/slog.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/slog/slog.go @@ -20,6 +20,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" "golang.org/x/tools/internal/typesinternal" ) @@ -114,10 +115,10 @@ func run(pass *analysis.Pass) (any, error) { default: if unknownArg == nil { pass.ReportRangef(arg, "%s arg %q should be a string or a slog.Attr (possible missing key or value)", - shortName(fn), analysisutil.Format(pass.Fset, arg)) + shortName(fn), analysisinternal.Format(pass.Fset, arg)) } else { pass.ReportRangef(arg, "%s arg %q should probably be a string or a slog.Attr (previous arg %q cannot be a key)", - shortName(fn), analysisutil.Format(pass.Fset, arg), analysisutil.Format(pass.Fset, unknownArg)) + shortName(fn), analysisinternal.Format(pass.Fset, arg), analysisinternal.Format(pass.Fset, unknownArg)) } // Stop here so we report at most one missing key per call. return @@ -157,7 +158,7 @@ func run(pass *analysis.Pass) (any, error) { } func isAttr(t types.Type) bool { - return analysisutil.IsNamedType(t, "log/slog", "Attr") + return analysisinternal.IsTypeNamed(t, "log/slog", "Attr") } // shortName returns a name for the function that is shorter than FullName. diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go index 75d8697759eb62..429125a8b7d428 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stdversion/stdversion.go @@ -11,6 +11,7 @@ import ( "go/build" "go/types" "regexp" + "slices" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" @@ -46,16 +47,14 @@ func run(pass *analysis.Pass) (any, error) { // Prior to go1.22, versions.FileVersion returns only the // toolchain version, which is of no use to us, so // disable this analyzer on earlier versions. - if !slicesContains(build.Default.ReleaseTags, "go1.22") { + if !slices.Contains(build.Default.ReleaseTags, "go1.22") { return nil, nil } // Don't report diagnostics for modules marked before go1.21, // since at that time the go directive wasn't clearly // specified as a toolchain requirement. - // - // TODO(adonovan): after go1.21, call GoVersion directly. - pkgVersion := any(pass.Pkg).(interface{ GoVersion() string }).GoVersion() + pkgVersion := pass.Pkg.GoVersion() if !versions.AtLeast(pkgVersion, "go1.21") { return nil, nil } @@ -88,7 +87,7 @@ func run(pass *analysis.Pass) (any, error) { inspect.Preorder(nodeFilter, func(n ast.Node) { switch n := n.(type) { case *ast.File: - if isGenerated(n) { + if ast.IsGenerated(n) { // Suppress diagnostics in generated files (such as cgo). fileVersion = "" } else { @@ -115,19 +114,6 @@ func run(pass *analysis.Pass) (any, error) { return nil, nil } -// Reduced from x/tools/gopls/internal/golang/util.go. Good enough for now. -// TODO(adonovan): use ast.IsGenerated in go1.21. -func isGenerated(f *ast.File) bool { - for _, group := range f.Comments { - for _, comment := range group.List { - if matched := generatedRx.MatchString(comment.Text); matched { - return true - } - } - } - return false -} - // Matches cgo generated comment as well as the proposed standard: // // https://golang.org/s/generatedcode @@ -147,13 +133,3 @@ func origin(obj types.Object) types.Object { } return obj } - -// TODO(adonovan): use go1.21 slices.Contains. -func slicesContains[S ~[]E, E comparable](slice S, x E) bool { - for _, elem := range slice { - if elem == x { - return true - } - } - return false -} diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go index 108600a2baf1a8..f56e6ecaa299b6 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go @@ -198,14 +198,14 @@ func run(pass *analysis.Pass) (interface{}, error) { // the type has methods, as some {String,GoString,Format} // may change the behavior of fmt.Sprint. if len(ttypes) == 1 && len(vtypes) == 1 && types.NewMethodSet(V0).Len() == 0 { - fmtName, importEdits := analysisinternal.AddImport(pass.TypesInfo, file, arg.Pos(), "fmt", "fmt") + _, prefix, importEdits := analysisinternal.AddImport(pass.TypesInfo, file, "fmt", "fmt", "Sprint", arg.Pos()) if types.Identical(T0, types.Typ[types.String]) { // string(x) -> fmt.Sprint(x) addFix("Format the number as a decimal", append(importEdits, analysis.TextEdit{ Pos: call.Fun.Pos(), End: call.Fun.End(), - NewText: []byte(fmtName + ".Sprint"), + NewText: []byte(prefix + "Sprint"), }), ) } else { @@ -214,7 +214,7 @@ func run(pass *analysis.Pass) (interface{}, error) { analysis.TextEdit{ Pos: call.Lparen + 1, End: call.Lparen + 1, - NewText: []byte(fmtName + ".Sprint("), + NewText: []byte(prefix + "Sprint("), }, analysis.TextEdit{ Pos: call.Rparen, diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go index a0beb46bd143b6..4115ef769430b2 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/structtag/structtag.go @@ -89,7 +89,7 @@ var checkTagSpaces = map[string]bool{"json": true, "xml": true, "asn1": true} // checkCanonicalFieldTag checks a single struct field tag. func checkCanonicalFieldTag(pass *analysis.Pass, field *types.Var, tag string, seen *namesSeen) { switch pass.Pkg.Path() { - case "encoding/json", "encoding/xml": + case "encoding/json", "encoding/json/v2", "encoding/xml": // These packages know how to use their own APIs. // Sometimes they are testing what happens to incorrect programs. return diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go index effcdc5700b16d..fef5a6014c41db 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go @@ -16,6 +16,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -38,7 +39,7 @@ var Analyzer = &analysis.Analyzer{ func run(pass *analysis.Pass) (interface{}, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - if !analysisutil.Imports(pass.Pkg, "testing") { + if !analysisinternal.Imports(pass.Pkg, "testing") { return nil, nil } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/util.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/util.go index 8c7a51ca525dda..027c99e6b0f931 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/util.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/util.go @@ -36,6 +36,8 @@ func localFunctionDecls(info *types.Info, files []*ast.File) func(*types.Func) * // isMethodNamed returns true if f is a method defined // in package with the path pkgPath with a name in names. +// +// (Unlike [analysisinternal.IsMethodNamed], it ignores the receiver type name.) func isMethodNamed(f *types.Func, pkgPath string, names ...string) bool { if f == nil { return false diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go index 36f2c43eb64d30..285b34218c3f16 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go @@ -16,6 +16,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -257,7 +258,7 @@ func isTestingType(typ types.Type, testingType string) bool { if !ok { return false } - return analysisutil.IsNamedType(ptr.Elem(), "testing", testingType) + return analysisinternal.IsTypeNamed(ptr.Elem(), "testing", testingType) } // Validate that fuzz target function's arguments are of accepted types. diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/timeformat/timeformat.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/timeformat/timeformat.go index 4a6c6b8bc6cc5c..4fdbb2b5415eff 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/timeformat/timeformat.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/timeformat/timeformat.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/analysisinternal" ) const badFormat = "2006-02-01" @@ -35,7 +36,7 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { // Note: (time.Time).Format is a method and can be a typeutil.Callee // without directly importing "time". So we cannot just skip this package // when !analysisutil.Imports(pass.Pkg, "time"). @@ -48,11 +49,9 @@ func run(pass *analysis.Pass) (interface{}, error) { } inspect.Preorder(nodeFilter, func(n ast.Node) { call := n.(*ast.CallExpr) - fn, ok := typeutil.Callee(pass.TypesInfo, call).(*types.Func) - if !ok { - return - } - if !isTimeDotFormat(fn) && !isTimeDotParse(fn) { + obj := typeutil.Callee(pass.TypesInfo, call) + if !analysisinternal.IsMethodNamed(obj, "time", "Time", "Format") && + !analysisinternal.IsFunctionNamed(obj, "time", "Parse") { return } if len(call.Args) > 0 { @@ -87,19 +86,6 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, nil } -func isTimeDotFormat(f *types.Func) bool { - if f.Name() != "Format" || f.Pkg() == nil || f.Pkg().Path() != "time" { - return false - } - // Verify that the receiver is time.Time. - recv := f.Type().(*types.Signature).Recv() - return recv != nil && analysisutil.IsNamedType(recv.Type(), "time", "Time") -} - -func isTimeDotParse(f *types.Func) bool { - return analysisutil.IsFunctionNamed(f, "time", "Parse") -} - // badFormatAt return the start of a bad format in e or -1 if no bad format is found. func badFormatAt(info *types.Info, e ast.Expr) int { tv, ok := info.Types[e] diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go index a7889fa4590837..26e894bd4000eb 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go @@ -28,7 +28,7 @@ var Analyzer = &analysis.Analyzer{ Run: run, } -func run(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (any, error) { switch pass.Pkg.Path() { case "encoding/gob", "encoding/json", "encoding/xml", "encoding/asn1": // These packages know how to use their own APIs. diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable/doc.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable/doc.go index d17d0d9444e1f4..325a15358d5939 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable/doc.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unreachable/doc.go @@ -9,6 +9,6 @@ // unreachable: check for unreachable code // // The unreachable analyzer finds statements that execution can never reach -// because they are preceded by an return statement, a call to panic, an +// because they are preceded by a return statement, a call to panic, an // infinite loop, or similar constructs. package unreachable diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unsafeptr/unsafeptr.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unsafeptr/unsafeptr.go index 272ae7fe045e3e..fb5b944faad08d 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unsafeptr/unsafeptr.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unsafeptr/unsafeptr.go @@ -16,6 +16,7 @@ import ( "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/analysisinternal" ) //go:embed doc.go @@ -104,7 +105,7 @@ func isSafeUintptr(info *types.Info, x ast.Expr) bool { } switch sel.Sel.Name { case "Pointer", "UnsafeAddr": - if analysisutil.IsNamedType(info.Types[sel.X].Type, "reflect", "Value") { + if analysisinternal.IsTypeNamed(info.Types[sel.X].Type, "reflect", "Value") { return true } } @@ -152,5 +153,5 @@ func hasBasicType(info *types.Info, x ast.Expr, kind types.BasicKind) bool { // isReflectHeader reports whether t is reflect.SliceHeader or reflect.StringHeader. func isReflectHeader(t types.Type) bool { - return analysisutil.IsNamedType(t, "reflect", "SliceHeader", "StringHeader") + return analysisinternal.IsTypeNamed(t, "reflect", "SliceHeader", "StringHeader") } diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go index c27d26dd6ec010..d7cc1e6ae2c7cf 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go @@ -131,7 +131,7 @@ func run(pass *analysis.Pass) (interface{}, error) { // func() string var sigNoArgsStringResult = types.NewSignature(nil, nil, - types.NewTuple(types.NewVar(token.NoPos, nil, "", types.Typ[types.String])), + types.NewTuple(types.NewParam(token.NoPos, nil, "", types.Typ[types.String])), false) type stringSetFlag map[string]bool diff --git a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go index 1a9b3094e5e577..82c3db6a39db74 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go +++ b/src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go @@ -367,17 +367,26 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re } pass := &analysis.Pass{ - Analyzer: a, - Fset: fset, - Files: files, - OtherFiles: cfg.NonGoFiles, - IgnoredFiles: cfg.IgnoredFiles, - Pkg: pkg, - TypesInfo: info, - TypesSizes: tc.Sizes, - TypeErrors: nil, // unitchecker doesn't RunDespiteErrors - ResultOf: inputs, - Report: func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) }, + Analyzer: a, + Fset: fset, + Files: files, + OtherFiles: cfg.NonGoFiles, + IgnoredFiles: cfg.IgnoredFiles, + Pkg: pkg, + TypesInfo: info, + TypesSizes: tc.Sizes, + TypeErrors: nil, // unitchecker doesn't RunDespiteErrors + ResultOf: inputs, + Report: func(d analysis.Diagnostic) { + // Unitchecker doesn't apply fixes, but it does report them in the JSON output. + if err := analysisinternal.ValidateFixes(fset, a, d.SuggestedFixes); err != nil { + // Since we have diagnostics, the exit code will be nonzero, + // so logging these errors is sufficient. + log.Println(err) + d.SuggestedFixes = nil + } + act.diagnostics = append(act.diagnostics, d) + }, ImportObjectFact: facts.ImportObjectFact, ExportObjectFact: facts.ExportObjectFact, AllObjectFacts: func() []analysis.ObjectFact { return facts.AllObjectFacts(factFilter) }, @@ -386,7 +395,7 @@ func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]re AllPackageFacts: func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) }, Module: module, } - pass.ReadFile = analysisinternal.MakeReadFile(pass) + pass.ReadFile = analysisinternal.CheckedReadFile(pass, os.ReadFile) t0 := time.Now() act.result, act.err = a.Run(pass) diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/inspector.go b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/inspector.go index 958cf38deb00a4..0d5050fe40510f 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/inspector.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/inspector.go @@ -36,6 +36,9 @@ package inspector import ( "go/ast" + _ "unsafe" + + "golang.org/x/tools/internal/astutil/edge" ) // An Inspector provides methods for inspecting @@ -44,6 +47,24 @@ type Inspector struct { events []event } +//go:linkname events +func events(in *Inspector) []event { return in.events } + +func packEdgeKindAndIndex(ek edge.Kind, index int) int32 { + return int32(uint32(index+1)<<7 | uint32(ek)) +} + +// unpackEdgeKindAndIndex unpacks the edge kind and edge index (within +// an []ast.Node slice) from the parent field of a pop event. +// +//go:linkname unpackEdgeKindAndIndex +func unpackEdgeKindAndIndex(x int32) (edge.Kind, int) { + // The "parent" field of a pop node holds the + // edge Kind in the lower 7 bits and the index+1 + // in the upper 25. + return edge.Kind(x & 0x7f), int(x>>7) - 1 +} + // New returns an Inspector for the specified syntax trees. func New(files []*ast.File) *Inspector { return &Inspector{traverse(files)} @@ -52,9 +73,10 @@ func New(files []*ast.File) *Inspector { // An event represents a push or a pop // of an ast.Node during a traversal. type event struct { - node ast.Node - typ uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events - index int // index of corresponding push or pop event + node ast.Node + typ uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events + index int32 // index of corresponding push or pop event + parent int32 // index of parent's push node (push nodes only), or packed edge kind/index (pop nodes only) } // TODO: Experiment with storing only the second word of event.node (unsafe.Pointer). @@ -83,7 +105,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { // }) mask := maskOf(types) - for i := 0; i < len(in.events); { + for i := int32(0); i < int32(len(in.events)); { ev := in.events[i] if ev.index > i { // push @@ -113,7 +135,7 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { // matches an element of the types slice. func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) { mask := maskOf(types) - for i := 0; i < len(in.events); { + for i := int32(0); i < int32(len(in.events)); { ev := in.events[i] if ev.index > i { // push @@ -147,7 +169,7 @@ func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proc func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) { mask := maskOf(types) var stack []ast.Node - for i := 0; i < len(in.events); { + for i := int32(0); i < int32(len(in.events)); { ev := in.events[i] if ev.index > i { // push @@ -189,43 +211,74 @@ func traverse(files []*ast.File) []event { extent += int(f.End() - f.Pos()) } // This estimate is based on the net/http package. - capacity := extent * 33 / 100 - if capacity > 1e6 { - capacity = 1e6 // impose some reasonable maximum + capacity := min(extent*33/100, 1e6) // impose some reasonable maximum (1M) + + v := &visitor{ + events: make([]event, 0, capacity), + stack: []item{{index: -1}}, // include an extra event so file nodes have a parent + } + for _, file := range files { + walk(v, edge.Invalid, -1, file) } - events := make([]event, 0, capacity) + return v.events +} - var stack []event - stack = append(stack, event{}) // include an extra event so file nodes have a parent - for _, f := range files { - ast.Inspect(f, func(n ast.Node) bool { - if n != nil { - // push - ev := event{ - node: n, - typ: 0, // temporarily used to accumulate type bits of subtree - index: len(events), // push event temporarily holds own index - } - stack = append(stack, ev) - events = append(events, ev) - } else { - // pop - top := len(stack) - 1 - ev := stack[top] - typ := typeOf(ev.node) - push := ev.index - parent := top - 1 - - events[push].typ = typ // set type of push - stack[parent].typ |= typ | ev.typ // parent's typ contains push and pop's typs. - events[push].index = len(events) // make push refer to pop - - stack = stack[:top] - events = append(events, ev) - } - return true - }) +type visitor struct { + events []event + stack []item +} + +type item struct { + index int32 // index of current node's push event + parentIndex int32 // index of parent node's push event + typAccum uint64 // accumulated type bits of current node's descendents + edgeKindAndIndex int32 // edge.Kind and index, bit packed +} + +func (v *visitor) push(ek edge.Kind, eindex int, node ast.Node) { + var ( + index = int32(len(v.events)) + parentIndex = v.stack[len(v.stack)-1].index + ) + v.events = append(v.events, event{ + node: node, + parent: parentIndex, + typ: typeOf(node), + index: 0, // (pop index is set later by visitor.pop) + }) + v.stack = append(v.stack, item{ + index: index, + parentIndex: parentIndex, + edgeKindAndIndex: packEdgeKindAndIndex(ek, eindex), + }) + + // 2B nodes ought to be enough for anyone! + if int32(len(v.events)) < 0 { + panic("event index exceeded int32") } - return events + // 32M elements in an []ast.Node ought to be enough for anyone! + if ek2, eindex2 := unpackEdgeKindAndIndex(packEdgeKindAndIndex(ek, eindex)); ek2 != ek || eindex2 != eindex { + panic("Node slice index exceeded uint25") + } +} + +func (v *visitor) pop(node ast.Node) { + top := len(v.stack) - 1 + current := v.stack[top] + + push := &v.events[current.index] + parent := &v.stack[top-1] + + push.index = int32(len(v.events)) // make push event refer to pop + parent.typAccum |= current.typAccum | push.typ // accumulate type bits into parent + + v.stack = v.stack[:top] + + v.events = append(v.events, event{ + node: node, + typ: current.typAccum, + index: current.index, + parent: current.edgeKindAndIndex, // see [unpackEdgeKindAndIndex] + }) } diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/iter.go b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/iter.go index b7e959114cb0b4..c576dc70ac7093 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/iter.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/iter.go @@ -26,7 +26,7 @@ func (in *Inspector) PreorderSeq(types ...ast.Node) iter.Seq[ast.Node] { return func(yield func(ast.Node) bool) { mask := maskOf(types) - for i := 0; i < len(in.events); { + for i := int32(0); i < int32(len(in.events)); { ev := in.events[i] if ev.index > i { // push @@ -63,7 +63,7 @@ func All[N interface { mask := typeOf((N)(nil)) return func(yield func(N) bool) { - for i := 0; i < len(in.events); { + for i := int32(0); i < int32(len(in.events)); { ev := in.events[i] if ev.index > i { // push diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go index 2a872f89d47d23..97784484578d50 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/typeof.go @@ -12,6 +12,8 @@ package inspector import ( "go/ast" "math" + + _ "unsafe" ) const ( @@ -215,8 +217,9 @@ func typeOf(n ast.Node) uint64 { return 0 } +//go:linkname maskOf func maskOf(nodes []ast.Node) uint64 { - if nodes == nil { + if len(nodes) == 0 { return math.MaxUint64 // match all node types } var mask uint64 diff --git a/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/walk.go b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/walk.go new file mode 100644 index 00000000000000..5a42174a0a0077 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/go/ast/inspector/walk.go @@ -0,0 +1,341 @@ +// Copyright 2025 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. + +package inspector + +// This file is a fork of ast.Inspect to reduce unnecessary dynamic +// calls and to gather edge information. +// +// Consistency with the original is ensured by TestInspectAllNodes. + +import ( + "fmt" + "go/ast" + + "golang.org/x/tools/internal/astutil/edge" +) + +func walkList[N ast.Node](v *visitor, ek edge.Kind, list []N) { + for i, node := range list { + walk(v, ek, i, node) + } +} + +func walk(v *visitor, ek edge.Kind, index int, node ast.Node) { + v.push(ek, index, node) + + // walk children + // (the order of the cases matches the order + // of the corresponding node types in ast.go) + switch n := node.(type) { + // Comments and fields + case *ast.Comment: + // nothing to do + + case *ast.CommentGroup: + walkList(v, edge.CommentGroup_List, n.List) + + case *ast.Field: + if n.Doc != nil { + walk(v, edge.Field_Doc, -1, n.Doc) + } + walkList(v, edge.Field_Names, n.Names) + if n.Type != nil { + walk(v, edge.Field_Type, -1, n.Type) + } + if n.Tag != nil { + walk(v, edge.Field_Tag, -1, n.Tag) + } + if n.Comment != nil { + walk(v, edge.Field_Comment, -1, n.Comment) + } + + case *ast.FieldList: + walkList(v, edge.FieldList_List, n.List) + + // Expressions + case *ast.BadExpr, *ast.Ident, *ast.BasicLit: + // nothing to do + + case *ast.Ellipsis: + if n.Elt != nil { + walk(v, edge.Ellipsis_Elt, -1, n.Elt) + } + + case *ast.FuncLit: + walk(v, edge.FuncLit_Type, -1, n.Type) + walk(v, edge.FuncLit_Body, -1, n.Body) + + case *ast.CompositeLit: + if n.Type != nil { + walk(v, edge.CompositeLit_Type, -1, n.Type) + } + walkList(v, edge.CompositeLit_Elts, n.Elts) + + case *ast.ParenExpr: + walk(v, edge.ParenExpr_X, -1, n.X) + + case *ast.SelectorExpr: + walk(v, edge.SelectorExpr_X, -1, n.X) + walk(v, edge.SelectorExpr_Sel, -1, n.Sel) + + case *ast.IndexExpr: + walk(v, edge.IndexExpr_X, -1, n.X) + walk(v, edge.IndexExpr_Index, -1, n.Index) + + case *ast.IndexListExpr: + walk(v, edge.IndexListExpr_X, -1, n.X) + walkList(v, edge.IndexListExpr_Indices, n.Indices) + + case *ast.SliceExpr: + walk(v, edge.SliceExpr_X, -1, n.X) + if n.Low != nil { + walk(v, edge.SliceExpr_Low, -1, n.Low) + } + if n.High != nil { + walk(v, edge.SliceExpr_High, -1, n.High) + } + if n.Max != nil { + walk(v, edge.SliceExpr_Max, -1, n.Max) + } + + case *ast.TypeAssertExpr: + walk(v, edge.TypeAssertExpr_X, -1, n.X) + if n.Type != nil { + walk(v, edge.TypeAssertExpr_Type, -1, n.Type) + } + + case *ast.CallExpr: + walk(v, edge.CallExpr_Fun, -1, n.Fun) + walkList(v, edge.CallExpr_Args, n.Args) + + case *ast.StarExpr: + walk(v, edge.StarExpr_X, -1, n.X) + + case *ast.UnaryExpr: + walk(v, edge.UnaryExpr_X, -1, n.X) + + case *ast.BinaryExpr: + walk(v, edge.BinaryExpr_X, -1, n.X) + walk(v, edge.BinaryExpr_Y, -1, n.Y) + + case *ast.KeyValueExpr: + walk(v, edge.KeyValueExpr_Key, -1, n.Key) + walk(v, edge.KeyValueExpr_Value, -1, n.Value) + + // Types + case *ast.ArrayType: + if n.Len != nil { + walk(v, edge.ArrayType_Len, -1, n.Len) + } + walk(v, edge.ArrayType_Elt, -1, n.Elt) + + case *ast.StructType: + walk(v, edge.StructType_Fields, -1, n.Fields) + + case *ast.FuncType: + if n.TypeParams != nil { + walk(v, edge.FuncType_TypeParams, -1, n.TypeParams) + } + if n.Params != nil { + walk(v, edge.FuncType_Params, -1, n.Params) + } + if n.Results != nil { + walk(v, edge.FuncType_Results, -1, n.Results) + } + + case *ast.InterfaceType: + walk(v, edge.InterfaceType_Methods, -1, n.Methods) + + case *ast.MapType: + walk(v, edge.MapType_Key, -1, n.Key) + walk(v, edge.MapType_Value, -1, n.Value) + + case *ast.ChanType: + walk(v, edge.ChanType_Value, -1, n.Value) + + // Statements + case *ast.BadStmt: + // nothing to do + + case *ast.DeclStmt: + walk(v, edge.DeclStmt_Decl, -1, n.Decl) + + case *ast.EmptyStmt: + // nothing to do + + case *ast.LabeledStmt: + walk(v, edge.LabeledStmt_Label, -1, n.Label) + walk(v, edge.LabeledStmt_Stmt, -1, n.Stmt) + + case *ast.ExprStmt: + walk(v, edge.ExprStmt_X, -1, n.X) + + case *ast.SendStmt: + walk(v, edge.SendStmt_Chan, -1, n.Chan) + walk(v, edge.SendStmt_Value, -1, n.Value) + + case *ast.IncDecStmt: + walk(v, edge.IncDecStmt_X, -1, n.X) + + case *ast.AssignStmt: + walkList(v, edge.AssignStmt_Lhs, n.Lhs) + walkList(v, edge.AssignStmt_Rhs, n.Rhs) + + case *ast.GoStmt: + walk(v, edge.GoStmt_Call, -1, n.Call) + + case *ast.DeferStmt: + walk(v, edge.DeferStmt_Call, -1, n.Call) + + case *ast.ReturnStmt: + walkList(v, edge.ReturnStmt_Results, n.Results) + + case *ast.BranchStmt: + if n.Label != nil { + walk(v, edge.BranchStmt_Label, -1, n.Label) + } + + case *ast.BlockStmt: + walkList(v, edge.BlockStmt_List, n.List) + + case *ast.IfStmt: + if n.Init != nil { + walk(v, edge.IfStmt_Init, -1, n.Init) + } + walk(v, edge.IfStmt_Cond, -1, n.Cond) + walk(v, edge.IfStmt_Body, -1, n.Body) + if n.Else != nil { + walk(v, edge.IfStmt_Else, -1, n.Else) + } + + case *ast.CaseClause: + walkList(v, edge.CaseClause_List, n.List) + walkList(v, edge.CaseClause_Body, n.Body) + + case *ast.SwitchStmt: + if n.Init != nil { + walk(v, edge.SwitchStmt_Init, -1, n.Init) + } + if n.Tag != nil { + walk(v, edge.SwitchStmt_Tag, -1, n.Tag) + } + walk(v, edge.SwitchStmt_Body, -1, n.Body) + + case *ast.TypeSwitchStmt: + if n.Init != nil { + walk(v, edge.TypeSwitchStmt_Init, -1, n.Init) + } + walk(v, edge.TypeSwitchStmt_Assign, -1, n.Assign) + walk(v, edge.TypeSwitchStmt_Body, -1, n.Body) + + case *ast.CommClause: + if n.Comm != nil { + walk(v, edge.CommClause_Comm, -1, n.Comm) + } + walkList(v, edge.CommClause_Body, n.Body) + + case *ast.SelectStmt: + walk(v, edge.SelectStmt_Body, -1, n.Body) + + case *ast.ForStmt: + if n.Init != nil { + walk(v, edge.ForStmt_Init, -1, n.Init) + } + if n.Cond != nil { + walk(v, edge.ForStmt_Cond, -1, n.Cond) + } + if n.Post != nil { + walk(v, edge.ForStmt_Post, -1, n.Post) + } + walk(v, edge.ForStmt_Body, -1, n.Body) + + case *ast.RangeStmt: + if n.Key != nil { + walk(v, edge.RangeStmt_Key, -1, n.Key) + } + if n.Value != nil { + walk(v, edge.RangeStmt_Value, -1, n.Value) + } + walk(v, edge.RangeStmt_X, -1, n.X) + walk(v, edge.RangeStmt_Body, -1, n.Body) + + // Declarations + case *ast.ImportSpec: + if n.Doc != nil { + walk(v, edge.ImportSpec_Doc, -1, n.Doc) + } + if n.Name != nil { + walk(v, edge.ImportSpec_Name, -1, n.Name) + } + walk(v, edge.ImportSpec_Path, -1, n.Path) + if n.Comment != nil { + walk(v, edge.ImportSpec_Comment, -1, n.Comment) + } + + case *ast.ValueSpec: + if n.Doc != nil { + walk(v, edge.ValueSpec_Doc, -1, n.Doc) + } + walkList(v, edge.ValueSpec_Names, n.Names) + if n.Type != nil { + walk(v, edge.ValueSpec_Type, -1, n.Type) + } + walkList(v, edge.ValueSpec_Values, n.Values) + if n.Comment != nil { + walk(v, edge.ValueSpec_Comment, -1, n.Comment) + } + + case *ast.TypeSpec: + if n.Doc != nil { + walk(v, edge.TypeSpec_Doc, -1, n.Doc) + } + walk(v, edge.TypeSpec_Name, -1, n.Name) + if n.TypeParams != nil { + walk(v, edge.TypeSpec_TypeParams, -1, n.TypeParams) + } + walk(v, edge.TypeSpec_Type, -1, n.Type) + if n.Comment != nil { + walk(v, edge.TypeSpec_Comment, -1, n.Comment) + } + + case *ast.BadDecl: + // nothing to do + + case *ast.GenDecl: + if n.Doc != nil { + walk(v, edge.GenDecl_Doc, -1, n.Doc) + } + walkList(v, edge.GenDecl_Specs, n.Specs) + + case *ast.FuncDecl: + if n.Doc != nil { + walk(v, edge.FuncDecl_Doc, -1, n.Doc) + } + if n.Recv != nil { + walk(v, edge.FuncDecl_Recv, -1, n.Recv) + } + walk(v, edge.FuncDecl_Name, -1, n.Name) + walk(v, edge.FuncDecl_Type, -1, n.Type) + if n.Body != nil { + walk(v, edge.FuncDecl_Body, -1, n.Body) + } + + case *ast.File: + if n.Doc != nil { + walk(v, edge.File_Doc, -1, n.Doc) + } + walk(v, edge.File_Name, -1, n.Name) + walkList(v, edge.File_Decls, n.Decls) + // don't walk n.Comments - they have been + // visited already through the individual + // nodes + + default: + // (includes *ast.Package) + panic(fmt.Sprintf("Walk: unexpected node type %T", n)) + } + + v.pop(node) +} diff --git a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go index 8d824f7140f456..b6d542c64ee64a 100644 --- a/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go +++ b/src/cmd/vendor/golang.org/x/tools/go/types/typeutil/map.go @@ -2,30 +2,35 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package typeutil defines various utilities for types, such as Map, -// a mapping from types.Type to any values. -package typeutil // import "golang.org/x/tools/go/types/typeutil" +// Package typeutil defines various utilities for types, such as [Map], +// a hash table that maps [types.Type] to any value. +package typeutil import ( "bytes" "fmt" "go/types" - "reflect" + "hash/maphash" + "unsafe" "golang.org/x/tools/internal/typeparams" ) // Map is a hash-table-based mapping from types (types.Type) to -// arbitrary any values. The concrete types that implement +// arbitrary values. The concrete types that implement // the Type interface are pointers. Since they are not canonicalized, // == cannot be used to check for equivalence, and thus we cannot // simply use a Go map. // // Just as with map[K]V, a nil *Map is a valid empty map. // -// Not thread-safe. +// Read-only map operations ([Map.At], [Map.Len], and so on) may +// safely be called concurrently. +// +// TODO(adonovan): deprecate in favor of https://go.dev/issues/69420 +// and 69559, if the latter proposals for a generic hash-map type and +// a types.Hash function are accepted. type Map struct { - hasher Hasher // shared by many Maps table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused length int // number of map entries } @@ -36,35 +41,17 @@ type entry struct { value any } -// SetHasher sets the hasher used by Map. -// -// All Hashers are functionally equivalent but contain internal state -// used to cache the results of hashing previously seen types. -// -// A single Hasher created by MakeHasher() may be shared among many -// Maps. This is recommended if the instances have many keys in -// common, as it will amortize the cost of hash computation. -// -// A Hasher may grow without bound as new types are seen. Even when a -// type is deleted from the map, the Hasher never shrinks, since other -// types in the map may reference the deleted type indirectly. +// SetHasher has no effect. // -// Hashers are not thread-safe, and read-only operations such as -// Map.Lookup require updates to the hasher, so a full Mutex lock (not a -// read-lock) is require around all Map operations if a shared -// hasher is accessed from multiple threads. -// -// If SetHasher is not called, the Map will create a private hasher at -// the first call to Insert. -func (m *Map) SetHasher(hasher Hasher) { - m.hasher = hasher -} +// It is a relic of an optimization that is no longer profitable. Do +// not use [Hasher], [MakeHasher], or [SetHasher] in new code. +func (m *Map) SetHasher(Hasher) {} // Delete removes the entry with the given key, if any. // It returns true if the entry was found. func (m *Map) Delete(key types.Type) bool { if m != nil && m.table != nil { - hash := m.hasher.Hash(key) + hash := hash(key) bucket := m.table[hash] for i, e := range bucket { if e.key != nil && types.Identical(key, e.key) { @@ -83,7 +70,7 @@ func (m *Map) Delete(key types.Type) bool { // The result is nil if the entry is not present. func (m *Map) At(key types.Type) any { if m != nil && m.table != nil { - for _, e := range m.table[m.hasher.Hash(key)] { + for _, e := range m.table[hash(key)] { if e.key != nil && types.Identical(key, e.key) { return e.value } @@ -96,7 +83,7 @@ func (m *Map) At(key types.Type) any { // and returns the previous entry, if any. func (m *Map) Set(key types.Type, value any) (prev any) { if m.table != nil { - hash := m.hasher.Hash(key) + hash := hash(key) bucket := m.table[hash] var hole *entry for i, e := range bucket { @@ -115,10 +102,7 @@ func (m *Map) Set(key types.Type, value any) (prev any) { m.table[hash] = append(bucket, entry{key, value}) } } else { - if m.hasher.memo == nil { - m.hasher = MakeHasher() - } - hash := m.hasher.Hash(key) + hash := hash(key) m.table = map[uint32][]entry{hash: {entry{key, value}}} } @@ -195,53 +179,35 @@ func (m *Map) KeysString() string { return m.toString(false) } -//////////////////////////////////////////////////////////////////////// -// Hasher - -// A Hasher maps each type to its hash value. -// For efficiency, a hasher uses memoization; thus its memory -// footprint grows monotonically over time. -// Hashers are not thread-safe. -// Hashers have reference semantics. -// Call MakeHasher to create a Hasher. -type Hasher struct { - memo map[types.Type]uint32 - - // ptrMap records pointer identity. - ptrMap map[any]uint32 - - // sigTParams holds type parameters from the signature being hashed. - // Signatures are considered identical modulo renaming of type parameters, so - // within the scope of a signature type the identity of the signature's type - // parameters is just their index. - // - // Since the language does not currently support referring to uninstantiated - // generic types or functions, and instantiated signatures do not have type - // parameter lists, we should never encounter a second non-empty type - // parameter list when hashing a generic signature. - sigTParams *types.TypeParamList -} +// -- Hasher -- -// MakeHasher returns a new Hasher instance. -func MakeHasher() Hasher { - return Hasher{ - memo: make(map[types.Type]uint32), - ptrMap: make(map[any]uint32), - sigTParams: nil, - } +// hash returns the hash of type t. +// TODO(adonovan): replace by types.Hash when Go proposal #69420 is accepted. +func hash(t types.Type) uint32 { + return theHasher.Hash(t) } +// A Hasher provides a [Hasher.Hash] method to map a type to its hash value. +// Hashers are stateless, and all are equivalent. +type Hasher struct{} + +var theHasher Hasher + +// MakeHasher returns Hasher{}. +// Hashers are stateless; all are equivalent. +func MakeHasher() Hasher { return theHasher } + // Hash computes a hash value for the given type t such that // Identical(t, t') => Hash(t) == Hash(t'). func (h Hasher) Hash(t types.Type) uint32 { - hash, ok := h.memo[t] - if !ok { - hash = h.hashFor(t) - h.memo[t] = hash - } - return hash + return hasher{inGenericSig: false}.hash(t) } +// hasher holds the state of a single Hash traversal: whether we are +// inside the signature of a generic function; this is used to +// optimize [hasher.hashTypeParam]. +type hasher struct{ inGenericSig bool } + // hashString computes the Fowler–Noll–Vo hash of s. func hashString(s string) uint32 { var h uint32 @@ -252,21 +218,21 @@ func hashString(s string) uint32 { return h } -// hashFor computes the hash of t. -func (h Hasher) hashFor(t types.Type) uint32 { +// hash computes the hash of t. +func (h hasher) hash(t types.Type) uint32 { // See Identical for rationale. switch t := t.(type) { case *types.Basic: return uint32(t.Kind()) case *types.Alias: - return h.Hash(types.Unalias(t)) + return h.hash(types.Unalias(t)) case *types.Array: - return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem()) + return 9043 + 2*uint32(t.Len()) + 3*h.hash(t.Elem()) case *types.Slice: - return 9049 + 2*h.Hash(t.Elem()) + return 9049 + 2*h.hash(t.Elem()) case *types.Struct: var hash uint32 = 9059 @@ -277,12 +243,12 @@ func (h Hasher) hashFor(t types.Type) uint32 { } hash += hashString(t.Tag(i)) hash += hashString(f.Name()) // (ignore f.Pkg) - hash += h.Hash(f.Type()) + hash += h.hash(f.Type()) } return hash case *types.Pointer: - return 9067 + 2*h.Hash(t.Elem()) + return 9067 + 2*h.hash(t.Elem()) case *types.Signature: var hash uint32 = 9091 @@ -290,33 +256,14 @@ func (h Hasher) hashFor(t types.Type) uint32 { hash *= 8863 } - // Use a separate hasher for types inside of the signature, where type - // parameter identity is modified to be (index, constraint). We must use a - // new memo for this hasher as type identity may be affected by this - // masking. For example, in func[T any](*T), the identity of *T depends on - // whether we are mapping the argument in isolation, or recursively as part - // of hashing the signature. - // - // We should never encounter a generic signature while hashing another - // generic signature, but defensively set sigTParams only if h.mask is - // unset. tparams := t.TypeParams() - if h.sigTParams == nil && tparams.Len() != 0 { - h = Hasher{ - // There may be something more efficient than discarding the existing - // memo, but it would require detecting whether types are 'tainted' by - // references to type parameters. - memo: make(map[types.Type]uint32), - // Re-using ptrMap ensures that pointer identity is preserved in this - // hasher. - ptrMap: h.ptrMap, - sigTParams: tparams, - } - } + if n := tparams.Len(); n > 0 { + h.inGenericSig = true // affects constraints, params, and results - for i := 0; i < tparams.Len(); i++ { - tparam := tparams.At(i) - hash += 7 * h.Hash(tparam.Constraint()) + for i := range n { + tparam := tparams.At(i) + hash += 7 * h.hash(tparam.Constraint()) + } } return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results()) @@ -350,17 +297,17 @@ func (h Hasher) hashFor(t types.Type) uint32 { return hash case *types.Map: - return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem()) + return 9109 + 2*h.hash(t.Key()) + 3*h.hash(t.Elem()) case *types.Chan: - return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem()) + return 9127 + 2*uint32(t.Dir()) + 3*h.hash(t.Elem()) case *types.Named: - hash := h.hashPtr(t.Obj()) + hash := h.hashTypeName(t.Obj()) targs := t.TypeArgs() for i := 0; i < targs.Len(); i++ { targ := targs.At(i) - hash += 2 * h.Hash(targ) + hash += 2 * h.hash(targ) } return hash @@ -374,17 +321,17 @@ func (h Hasher) hashFor(t types.Type) uint32 { panic(fmt.Sprintf("%T: %v", t, t)) } -func (h Hasher) hashTuple(tuple *types.Tuple) uint32 { +func (h hasher) hashTuple(tuple *types.Tuple) uint32 { // See go/types.identicalTypes for rationale. n := tuple.Len() hash := 9137 + 2*uint32(n) - for i := 0; i < n; i++ { - hash += 3 * h.Hash(tuple.At(i).Type()) + for i := range n { + hash += 3 * h.hash(tuple.At(i).Type()) } return hash } -func (h Hasher) hashUnion(t *types.Union) uint32 { +func (h hasher) hashUnion(t *types.Union) uint32 { // Hash type restrictions. terms, err := typeparams.UnionTermSet(t) // if err != nil t has invalid type restrictions. Fall back on a non-zero @@ -395,11 +342,11 @@ func (h Hasher) hashUnion(t *types.Union) uint32 { return h.hashTermSet(terms) } -func (h Hasher) hashTermSet(terms []*types.Term) uint32 { +func (h hasher) hashTermSet(terms []*types.Term) uint32 { hash := 9157 + 2*uint32(len(terms)) for _, term := range terms { // term order is not significant. - termHash := h.Hash(term.Type()) + termHash := h.hash(term.Type()) if term.Tilde() { termHash *= 9161 } @@ -408,36 +355,47 @@ func (h Hasher) hashTermSet(terms []*types.Term) uint32 { return hash } -// hashTypeParam returns a hash of the type parameter t, with a hash value -// depending on whether t is contained in h.sigTParams. -// -// If h.sigTParams is set and contains t, then we are in the process of hashing -// a signature, and the hash value of t must depend only on t's index and -// constraint: signatures are considered identical modulo type parameter -// renaming. To avoid infinite recursion, we only hash the type parameter -// index, and rely on types.Identical to handle signatures where constraints -// are not identical. -// -// Otherwise the hash of t depends only on t's pointer identity. -func (h Hasher) hashTypeParam(t *types.TypeParam) uint32 { - if h.sigTParams != nil { - i := t.Index() - if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) { - return 9173 + 3*uint32(i) - } +// hashTypeParam returns the hash of a type parameter. +func (h hasher) hashTypeParam(t *types.TypeParam) uint32 { + // Within the signature of a generic function, TypeParams are + // identical if they have the same index and constraint, so we + // hash them based on index. + // + // When we are outside a generic function, free TypeParams are + // identical iff they are the same object, so we can use a + // more discriminating hash consistent with object identity. + // This optimization saves [Map] about 4% when hashing all the + // types.Info.Types in the forward closure of net/http. + if !h.inGenericSig { + // Optimization: outside a generic function signature, + // use a more discrimating hash consistent with object identity. + return h.hashTypeName(t.Obj()) } - return h.hashPtr(t.Obj()) + return 9173 + 3*uint32(t.Index()) } -// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that -// pointers values are not dependent on the GC. -func (h Hasher) hashPtr(ptr any) uint32 { - if hash, ok := h.ptrMap[ptr]; ok { - return hash +var theSeed = maphash.MakeSeed() + +// hashTypeName hashes the pointer of tname. +func (hasher) hashTypeName(tname *types.TypeName) uint32 { + // Since types.Identical uses == to compare TypeNames, + // the Hash function uses maphash.Comparable. + // TODO(adonovan): or will, when it becomes available in go1.24. + // In the meantime we use the pointer's numeric value. + // + // hash := maphash.Comparable(theSeed, tname) + // + // (Another approach would be to hash the name and package + // path, and whether or not it is a package-level typename. It + // is rare for a package to define multiple local types with + // the same name.) + ptr := uintptr(unsafe.Pointer(tname)) + if unsafe.Sizeof(ptr) == 8 { + hash := uint64(ptr) + return uint32(hash ^ (hash >> 32)) + } else { + return uint32(ptr) } - hash := uint32(reflect.ValueOf(ptr).Pointer()) - h.ptrMap[ptr] = hash - return hash } // shallowHash computes a hash of t without looking at any of its @@ -454,7 +412,7 @@ func (h Hasher) hashPtr(ptr any) uint32 { // include m itself; there is no mention of the named type X that // might help us break the cycle. // (See comment in go/types.identical, case *Interface, for more.) -func (h Hasher) shallowHash(t types.Type) uint32 { +func (h hasher) shallowHash(t types.Type) uint32 { // t is the type of an interface method (Signature), // its params or results (Tuples), or their immediate // elements (mostly Slice, Pointer, Basic, Named), @@ -475,7 +433,7 @@ func (h Hasher) shallowHash(t types.Type) uint32 { case *types.Tuple: n := t.Len() hash := 9137 + 2*uint32(n) - for i := 0; i < n; i++ { + for i := range n { hash += 53471161 * h.shallowHash(t.At(i).Type()) } return hash @@ -508,10 +466,10 @@ func (h Hasher) shallowHash(t types.Type) uint32 { return 9127 case *types.Named: - return h.hashPtr(t.Obj()) + return h.hashTypeName(t.Obj()) case *types.TypeParam: - return h.hashPtr(t.Obj()) + return h.hashTypeParam(t) } panic(fmt.Sprintf("shallowHash: %T: %v", t, t)) } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go index fe67b0fa27a69f..d96d22982c5a72 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/analysisinternal/analysis.go @@ -8,15 +8,19 @@ package analysisinternal import ( "bytes" + "cmp" "fmt" "go/ast" + "go/printer" "go/scanner" "go/token" "go/types" - "os" pathpkg "path" + "slices" + "strings" "golang.org/x/tools/go/analysis" + "golang.org/x/tools/internal/typesinternal" ) func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos { @@ -65,90 +69,6 @@ func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos return end } -// StmtToInsertVarBefore returns the ast.Stmt before which we can -// safely insert a new var declaration, or nil if the path denotes a -// node outside any statement. -// -// Basic Example: -// -// z := 1 -// y := z + x -// -// If x is undeclared, then this function would return `y := z + x`, so that we -// can insert `x := ` on the line before `y := z + x`. -// -// If stmt example: -// -// if z == 1 { -// } else if z == y {} -// -// If y is undeclared, then this function would return `if z == 1 {`, because we cannot -// insert a statement between an if and an else if statement. As a result, we need to find -// the top of the if chain to insert `y := ` before. -func StmtToInsertVarBefore(path []ast.Node) ast.Stmt { - enclosingIndex := -1 - for i, p := range path { - if _, ok := p.(ast.Stmt); ok { - enclosingIndex = i - break - } - } - if enclosingIndex == -1 { - return nil // no enclosing statement: outside function - } - enclosingStmt := path[enclosingIndex] - switch enclosingStmt.(type) { - case *ast.IfStmt: - // The enclosingStmt is inside of the if declaration, - // We need to check if we are in an else-if stmt and - // get the base if statement. - // TODO(adonovan): for non-constants, it may be preferable - // to add the decl as the Init field of the innermost - // enclosing ast.IfStmt. - return baseIfStmt(path, enclosingIndex) - case *ast.CaseClause: - // Get the enclosing switch stmt if the enclosingStmt is - // inside of the case statement. - for i := enclosingIndex + 1; i < len(path); i++ { - if node, ok := path[i].(*ast.SwitchStmt); ok { - return node - } else if node, ok := path[i].(*ast.TypeSwitchStmt); ok { - return node - } - } - } - if len(path) <= enclosingIndex+1 { - return enclosingStmt.(ast.Stmt) - } - // Check if the enclosing statement is inside another node. - switch expr := path[enclosingIndex+1].(type) { - case *ast.IfStmt: - // Get the base if statement. - return baseIfStmt(path, enclosingIndex+1) - case *ast.ForStmt: - if expr.Init == enclosingStmt || expr.Post == enclosingStmt { - return expr - } - case *ast.SwitchStmt, *ast.TypeSwitchStmt: - return expr.(ast.Stmt) - } - return enclosingStmt.(ast.Stmt) -} - -// baseIfStmt walks up the if/else-if chain until we get to -// the top of the current if chain. -func baseIfStmt(path []ast.Node, index int) ast.Stmt { - stmt := path[index] - for i := index + 1; i < len(path); i++ { - if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt { - stmt = node - continue - } - break - } - return stmt.(ast.Stmt) -} - // WalkASTWithParent walks the AST rooted at n. The semantics are // similar to ast.Inspect except it does not call f(nil). func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) { @@ -258,20 +178,25 @@ func equivalentTypes(want, got types.Type) bool { return types.AssignableTo(want, got) } -// MakeReadFile returns a simple implementation of the Pass.ReadFile function. -func MakeReadFile(pass *analysis.Pass) func(filename string) ([]byte, error) { +// A ReadFileFunc is a function that returns the +// contents of a file, such as [os.ReadFile]. +type ReadFileFunc = func(filename string) ([]byte, error) + +// CheckedReadFile returns a wrapper around a Pass.ReadFile +// function that performs the appropriate checks. +func CheckedReadFile(pass *analysis.Pass, readFile ReadFileFunc) ReadFileFunc { return func(filename string) ([]byte, error) { if err := CheckReadable(pass, filename); err != nil { return nil, err } - return os.ReadFile(filename) + return readFile(filename) } } // CheckReadable enforces the access policy defined by the ReadFile field of [analysis.Pass]. func CheckReadable(pass *analysis.Pass, filename string) error { - if slicesContains(pass.OtherFiles, filename) || - slicesContains(pass.IgnoredFiles, filename) { + if slices.Contains(pass.OtherFiles, filename) || + slices.Contains(pass.IgnoredFiles, filename) { return nil } for _, f := range pass.Files { @@ -282,24 +207,21 @@ func CheckReadable(pass *analysis.Pass, filename string) error { return fmt.Errorf("Pass.ReadFile: %s is not among OtherFiles, IgnoredFiles, or names of Files", filename) } -// TODO(adonovan): use go1.21 slices.Contains. -func slicesContains[S ~[]E, E comparable](slice S, x E) bool { - for _, elem := range slice { - if elem == x { - return true - } - } - return false -} - // AddImport checks whether this file already imports pkgpath and // that import is in scope at pos. If so, it returns the name under // which it was imported and a zero edit. Otherwise, it adds a new // import of pkgpath, using a name derived from the preferred name, -// and returns the chosen name along with the edit for the new import. +// and returns the chosen name, a prefix to be concatenated with member +// to form a qualified name, and the edit for the new import. +// +// In the special case that pkgpath is dot-imported then member, the +// identifer for which the import is being added, is consulted. If +// member is not shadowed at pos, AddImport returns (".", "", nil). +// (AddImport accepts the caller's implicit claim that the imported +// package declares member.) // // It does not mutate its arguments. -func AddImport(info *types.Info, file *ast.File, pos token.Pos, pkgpath, preferredName string) (name string, newImport []analysis.TextEdit) { +func AddImport(info *types.Info, file *ast.File, preferredName, pkgpath, member string, pos token.Pos) (name, prefix string, newImport []analysis.TextEdit) { // Find innermost enclosing lexical block. scope := info.Scopes[file].Innermost(pos) if scope == nil { @@ -309,10 +231,16 @@ func AddImport(info *types.Info, file *ast.File, pos token.Pos, pkgpath, preferr // Is there an existing import of this package? // If so, are we in its scope? (not shadowed) for _, spec := range file.Imports { - pkgname, ok := importedPkgName(info, spec) - if ok && pkgname.Imported().Path() == pkgpath { - if _, obj := scope.LookupParent(pkgname.Name(), pos); obj == pkgname { - return pkgname.Name(), nil + pkgname := info.PkgNameOf(spec) + if pkgname != nil && pkgname.Imported().Path() == pkgpath { + name = pkgname.Name() + if name == "." { + // The scope of ident must be the file scope. + if s, _ := scope.LookupParent(member, pos); s == info.Scopes[file] { + return name, "", nil + } + } else if _, obj := scope.LookupParent(name, pos); obj == pkgname { + return name, name + ".", nil } } } @@ -327,16 +255,16 @@ func AddImport(info *types.Info, file *ast.File, pos token.Pos, pkgpath, preferr newName = fmt.Sprintf("%s%d", preferredName, i) } - // For now, keep it real simple: create a new import - // declaration before the first existing declaration (which - // must exist), including its comments, and let goimports tidy it up. + // Create a new import declaration either before the first existing + // declaration (which must exist), including its comments; or + // inside the declaration, if it is an import group. // // Use a renaming import whenever the preferred name is not // available, or the chosen name does not match the last // segment of its path. - newText := fmt.Sprintf("import %q\n\n", pkgpath) + newText := fmt.Sprintf("%q", pkgpath) if newName != preferredName || newName != pathpkg.Base(pkgpath) { - newText = fmt.Sprintf("import %s %q\n\n", newName, pkgpath) + newText = fmt.Sprintf("%s %q", newName, pkgpath) } decl0 := file.Decls[0] var before ast.Node = decl0 @@ -350,22 +278,209 @@ func AddImport(info *types.Info, file *ast.File, pos token.Pos, pkgpath, preferr before = decl0.Doc } } - return newName, []analysis.TextEdit{{ - Pos: before.Pos(), - End: before.Pos(), + // If the first decl is an import group, add this new import at the end. + if gd, ok := before.(*ast.GenDecl); ok && gd.Tok == token.IMPORT && gd.Rparen.IsValid() { + pos = gd.Rparen + newText = "\t" + newText + "\n" + } else { + pos = before.Pos() + newText = "import " + newText + "\n\n" + } + return newName, newName + ".", []analysis.TextEdit{{ + Pos: pos, + End: pos, NewText: []byte(newText), }} } -// importedPkgName returns the PkgName object declared by an ImportSpec. -// TODO(adonovan): use go1.22's Info.PkgNameOf. -func importedPkgName(info *types.Info, imp *ast.ImportSpec) (*types.PkgName, bool) { - var obj types.Object - if imp.Name != nil { - obj = info.Defs[imp.Name] - } else { - obj = info.Implicits[imp] +// Format returns a string representation of the expression e. +func Format(fset *token.FileSet, e ast.Expr) string { + var buf strings.Builder + printer.Fprint(&buf, fset, e) // ignore errors + return buf.String() +} + +// Imports returns true if path is imported by pkg. +func Imports(pkg *types.Package, path string) bool { + for _, imp := range pkg.Imports() { + if imp.Path() == path { + return true + } + } + return false +} + +// IsTypeNamed reports whether t is (or is an alias for) a +// package-level defined type with the given package path and one of +// the given names. It returns false if t is nil. +// +// This function avoids allocating the concatenation of "pkg.Name", +// which is important for the performance of syntax matching. +func IsTypeNamed(t types.Type, pkgPath string, names ...string) bool { + if named, ok := types.Unalias(t).(*types.Named); ok { + tname := named.Obj() + return tname != nil && + typesinternal.IsPackageLevel(tname) && + tname.Pkg().Path() == pkgPath && + slices.Contains(names, tname.Name()) + } + return false +} + +// IsPointerToNamed reports whether t is (or is an alias for) a pointer to a +// package-level defined type with the given package path and one of the given +// names. It returns false if t is not a pointer type. +func IsPointerToNamed(t types.Type, pkgPath string, names ...string) bool { + r := typesinternal.Unpointer(t) + if r == t { + return false + } + return IsTypeNamed(r, pkgPath, names...) +} + +// IsFunctionNamed reports whether obj is a package-level function +// defined in the given package and has one of the given names. +// It returns false if obj is nil. +// +// This function avoids allocating the concatenation of "pkg.Name", +// which is important for the performance of syntax matching. +func IsFunctionNamed(obj types.Object, pkgPath string, names ...string) bool { + f, ok := obj.(*types.Func) + return ok && + typesinternal.IsPackageLevel(obj) && + f.Pkg().Path() == pkgPath && + f.Type().(*types.Signature).Recv() == nil && + slices.Contains(names, f.Name()) +} + +// IsMethodNamed reports whether obj is a method defined on a +// package-level type with the given package and type name, and has +// one of the given names. It returns false if obj is nil. +// +// This function avoids allocating the concatenation of "pkg.TypeName.Name", +// which is important for the performance of syntax matching. +func IsMethodNamed(obj types.Object, pkgPath string, typeName string, names ...string) bool { + if fn, ok := obj.(*types.Func); ok { + if recv := fn.Type().(*types.Signature).Recv(); recv != nil { + _, T := typesinternal.ReceiverNamed(recv) + return T != nil && + IsTypeNamed(T, pkgPath, typeName) && + slices.Contains(names, fn.Name()) + } + } + return false +} + +// ValidateFixes validates the set of fixes for a single diagnostic. +// Any error indicates a bug in the originating analyzer. +// +// It updates fixes so that fixes[*].End.IsValid(). +// +// It may be used as part of an analysis driver implementation. +func ValidateFixes(fset *token.FileSet, a *analysis.Analyzer, fixes []analysis.SuggestedFix) error { + fixMessages := make(map[string]bool) + for i := range fixes { + fix := &fixes[i] + if fixMessages[fix.Message] { + return fmt.Errorf("analyzer %q suggests two fixes with same Message (%s)", a.Name, fix.Message) + } + fixMessages[fix.Message] = true + if err := validateFix(fset, fix); err != nil { + return fmt.Errorf("analyzer %q suggests invalid fix (%s): %v", a.Name, fix.Message, err) + } + } + return nil +} + +// validateFix validates a single fix. +// Any error indicates a bug in the originating analyzer. +// +// It updates fix so that fix.End.IsValid(). +func validateFix(fset *token.FileSet, fix *analysis.SuggestedFix) error { + + // Stably sort edits by Pos. This ordering puts insertions + // (end = start) before deletions (end > start) at the same + // point, but uses a stable sort to preserve the order of + // multiple insertions at the same point. + slices.SortStableFunc(fix.TextEdits, func(x, y analysis.TextEdit) int { + if sign := cmp.Compare(x.Pos, y.Pos); sign != 0 { + return sign + } + return cmp.Compare(x.End, y.End) + }) + + var prev *analysis.TextEdit + for i := range fix.TextEdits { + edit := &fix.TextEdits[i] + + // Validate edit individually. + start := edit.Pos + file := fset.File(start) + if file == nil { + return fmt.Errorf("missing file info for pos (%v)", edit.Pos) + } + if end := edit.End; end.IsValid() { + if end < start { + return fmt.Errorf("pos (%v) > end (%v)", edit.Pos, edit.End) + } + endFile := fset.File(end) + if endFile == nil { + return fmt.Errorf("malformed end position %v", end) + } + if endFile != file { + return fmt.Errorf("edit spans files %v and %v", file.Name(), endFile.Name()) + } + } else { + edit.End = start // update the SuggestedFix + } + if eof := token.Pos(file.Base() + file.Size()); edit.End > eof { + return fmt.Errorf("end is (%v) beyond end of file (%v)", edit.End, eof) + } + + // Validate the sequence of edits: + // properly ordered, no overlapping deletions + if prev != nil && edit.Pos < prev.End { + xpos := fset.Position(prev.Pos) + xend := fset.Position(prev.End) + ypos := fset.Position(edit.Pos) + yend := fset.Position(edit.End) + return fmt.Errorf("overlapping edits to %s (%d:%d-%d:%d and %d:%d-%d:%d)", + xpos.Filename, + xpos.Line, xpos.Column, + xend.Line, xend.Column, + ypos.Line, ypos.Column, + yend.Line, yend.Column, + ) + } + prev = edit + } + + return nil +} + +// CanImport reports whether one package is allowed to import another. +// +// TODO(adonovan): allow customization of the accessibility relation +// (e.g. for Bazel). +func CanImport(from, to string) bool { + // TODO(adonovan): better segment hygiene. + if to == "internal" || strings.HasPrefix(to, "internal/") { + // Special case: only std packages may import internal/... + // We can't reliably know whether we're in std, so we + // use a heuristic on the first segment. + first, _, _ := strings.Cut(from, "/") + if strings.Contains(first, ".") { + return false // example.com/foo ∉ std + } + if first == "testdata" { + return false // testdata/foo ∉ std + } + } + if strings.HasSuffix(to, "/internal") { + return strings.HasPrefix(from, to[:len(to)-len("/internal")]) + } + if i := strings.LastIndex(to, "/internal/"); i >= 0 { + return strings.HasPrefix(from, to[:i]) } - pkgname, ok := obj.(*types.PkgName) - return pkgname, ok + return true } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/astutil/edge/edge.go b/src/cmd/vendor/golang.org/x/tools/internal/astutil/edge/edge.go new file mode 100644 index 00000000000000..4f6ccfd6e5e293 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/astutil/edge/edge.go @@ -0,0 +1,295 @@ +// Copyright 2025 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. + +// Package edge defines identifiers for each field of an ast.Node +// struct type that refers to another Node. +package edge + +import ( + "fmt" + "go/ast" + "reflect" +) + +// A Kind describes a field of an ast.Node struct. +type Kind uint8 + +// String returns a description of the edge kind. +func (k Kind) String() string { + if k == Invalid { + return "" + } + info := fieldInfos[k] + return fmt.Sprintf("%v.%s", info.nodeType.Elem().Name(), info.name) +} + +// NodeType returns the pointer-to-struct type of the ast.Node implementation. +func (k Kind) NodeType() reflect.Type { return fieldInfos[k].nodeType } + +// FieldName returns the name of the field. +func (k Kind) FieldName() string { return fieldInfos[k].name } + +// FieldType returns the declared type of the field. +func (k Kind) FieldType() reflect.Type { return fieldInfos[k].fieldType } + +// Get returns the direct child of n identified by (k, idx). +// n's type must match k.NodeType(). +// idx must be a valid slice index, or -1 for a non-slice. +func (k Kind) Get(n ast.Node, idx int) ast.Node { + if k.NodeType() != reflect.TypeOf(n) { + panic(fmt.Sprintf("%v.Get(%T): invalid node type", k, n)) + } + v := reflect.ValueOf(n).Elem().Field(fieldInfos[k].index) + if idx != -1 { + v = v.Index(idx) // asserts valid index + } else { + // (The type assertion below asserts that v is not a slice.) + } + return v.Interface().(ast.Node) // may be nil +} + +const ( + Invalid Kind = iota // for nodes at the root of the traversal + + // Kinds are sorted alphabetically. + // Numbering is not stable. + // Each is named Type_Field, where Type is the + // ast.Node struct type and Field is the name of the field + + ArrayType_Elt + ArrayType_Len + AssignStmt_Lhs + AssignStmt_Rhs + BinaryExpr_X + BinaryExpr_Y + BlockStmt_List + BranchStmt_Label + CallExpr_Args + CallExpr_Fun + CaseClause_Body + CaseClause_List + ChanType_Value + CommClause_Body + CommClause_Comm + CommentGroup_List + CompositeLit_Elts + CompositeLit_Type + DeclStmt_Decl + DeferStmt_Call + Ellipsis_Elt + ExprStmt_X + FieldList_List + Field_Comment + Field_Doc + Field_Names + Field_Tag + Field_Type + File_Decls + File_Doc + File_Name + ForStmt_Body + ForStmt_Cond + ForStmt_Init + ForStmt_Post + FuncDecl_Body + FuncDecl_Doc + FuncDecl_Name + FuncDecl_Recv + FuncDecl_Type + FuncLit_Body + FuncLit_Type + FuncType_Params + FuncType_Results + FuncType_TypeParams + GenDecl_Doc + GenDecl_Specs + GoStmt_Call + IfStmt_Body + IfStmt_Cond + IfStmt_Else + IfStmt_Init + ImportSpec_Comment + ImportSpec_Doc + ImportSpec_Name + ImportSpec_Path + IncDecStmt_X + IndexExpr_Index + IndexExpr_X + IndexListExpr_Indices + IndexListExpr_X + InterfaceType_Methods + KeyValueExpr_Key + KeyValueExpr_Value + LabeledStmt_Label + LabeledStmt_Stmt + MapType_Key + MapType_Value + ParenExpr_X + RangeStmt_Body + RangeStmt_Key + RangeStmt_Value + RangeStmt_X + ReturnStmt_Results + SelectStmt_Body + SelectorExpr_Sel + SelectorExpr_X + SendStmt_Chan + SendStmt_Value + SliceExpr_High + SliceExpr_Low + SliceExpr_Max + SliceExpr_X + StarExpr_X + StructType_Fields + SwitchStmt_Body + SwitchStmt_Init + SwitchStmt_Tag + TypeAssertExpr_Type + TypeAssertExpr_X + TypeSpec_Comment + TypeSpec_Doc + TypeSpec_Name + TypeSpec_Type + TypeSpec_TypeParams + TypeSwitchStmt_Assign + TypeSwitchStmt_Body + TypeSwitchStmt_Init + UnaryExpr_X + ValueSpec_Comment + ValueSpec_Doc + ValueSpec_Names + ValueSpec_Type + ValueSpec_Values + + maxKind +) + +// Assert that the encoding fits in 7 bits, +// as the inspector relies on this. +// (We are currently at 104.) +var _ = [1 << 7]struct{}{}[maxKind] + +type fieldInfo struct { + nodeType reflect.Type // pointer-to-struct type of ast.Node implementation + name string + index int + fieldType reflect.Type +} + +func info[N ast.Node](fieldName string) fieldInfo { + nodePtrType := reflect.TypeFor[N]() + f, ok := nodePtrType.Elem().FieldByName(fieldName) + if !ok { + panic(fieldName) + } + return fieldInfo{nodePtrType, fieldName, f.Index[0], f.Type} +} + +var fieldInfos = [...]fieldInfo{ + Invalid: {}, + ArrayType_Elt: info[*ast.ArrayType]("Elt"), + ArrayType_Len: info[*ast.ArrayType]("Len"), + AssignStmt_Lhs: info[*ast.AssignStmt]("Lhs"), + AssignStmt_Rhs: info[*ast.AssignStmt]("Rhs"), + BinaryExpr_X: info[*ast.BinaryExpr]("X"), + BinaryExpr_Y: info[*ast.BinaryExpr]("Y"), + BlockStmt_List: info[*ast.BlockStmt]("List"), + BranchStmt_Label: info[*ast.BranchStmt]("Label"), + CallExpr_Args: info[*ast.CallExpr]("Args"), + CallExpr_Fun: info[*ast.CallExpr]("Fun"), + CaseClause_Body: info[*ast.CaseClause]("Body"), + CaseClause_List: info[*ast.CaseClause]("List"), + ChanType_Value: info[*ast.ChanType]("Value"), + CommClause_Body: info[*ast.CommClause]("Body"), + CommClause_Comm: info[*ast.CommClause]("Comm"), + CommentGroup_List: info[*ast.CommentGroup]("List"), + CompositeLit_Elts: info[*ast.CompositeLit]("Elts"), + CompositeLit_Type: info[*ast.CompositeLit]("Type"), + DeclStmt_Decl: info[*ast.DeclStmt]("Decl"), + DeferStmt_Call: info[*ast.DeferStmt]("Call"), + Ellipsis_Elt: info[*ast.Ellipsis]("Elt"), + ExprStmt_X: info[*ast.ExprStmt]("X"), + FieldList_List: info[*ast.FieldList]("List"), + Field_Comment: info[*ast.Field]("Comment"), + Field_Doc: info[*ast.Field]("Doc"), + Field_Names: info[*ast.Field]("Names"), + Field_Tag: info[*ast.Field]("Tag"), + Field_Type: info[*ast.Field]("Type"), + File_Decls: info[*ast.File]("Decls"), + File_Doc: info[*ast.File]("Doc"), + File_Name: info[*ast.File]("Name"), + ForStmt_Body: info[*ast.ForStmt]("Body"), + ForStmt_Cond: info[*ast.ForStmt]("Cond"), + ForStmt_Init: info[*ast.ForStmt]("Init"), + ForStmt_Post: info[*ast.ForStmt]("Post"), + FuncDecl_Body: info[*ast.FuncDecl]("Body"), + FuncDecl_Doc: info[*ast.FuncDecl]("Doc"), + FuncDecl_Name: info[*ast.FuncDecl]("Name"), + FuncDecl_Recv: info[*ast.FuncDecl]("Recv"), + FuncDecl_Type: info[*ast.FuncDecl]("Type"), + FuncLit_Body: info[*ast.FuncLit]("Body"), + FuncLit_Type: info[*ast.FuncLit]("Type"), + FuncType_Params: info[*ast.FuncType]("Params"), + FuncType_Results: info[*ast.FuncType]("Results"), + FuncType_TypeParams: info[*ast.FuncType]("TypeParams"), + GenDecl_Doc: info[*ast.GenDecl]("Doc"), + GenDecl_Specs: info[*ast.GenDecl]("Specs"), + GoStmt_Call: info[*ast.GoStmt]("Call"), + IfStmt_Body: info[*ast.IfStmt]("Body"), + IfStmt_Cond: info[*ast.IfStmt]("Cond"), + IfStmt_Else: info[*ast.IfStmt]("Else"), + IfStmt_Init: info[*ast.IfStmt]("Init"), + ImportSpec_Comment: info[*ast.ImportSpec]("Comment"), + ImportSpec_Doc: info[*ast.ImportSpec]("Doc"), + ImportSpec_Name: info[*ast.ImportSpec]("Name"), + ImportSpec_Path: info[*ast.ImportSpec]("Path"), + IncDecStmt_X: info[*ast.IncDecStmt]("X"), + IndexExpr_Index: info[*ast.IndexExpr]("Index"), + IndexExpr_X: info[*ast.IndexExpr]("X"), + IndexListExpr_Indices: info[*ast.IndexListExpr]("Indices"), + IndexListExpr_X: info[*ast.IndexListExpr]("X"), + InterfaceType_Methods: info[*ast.InterfaceType]("Methods"), + KeyValueExpr_Key: info[*ast.KeyValueExpr]("Key"), + KeyValueExpr_Value: info[*ast.KeyValueExpr]("Value"), + LabeledStmt_Label: info[*ast.LabeledStmt]("Label"), + LabeledStmt_Stmt: info[*ast.LabeledStmt]("Stmt"), + MapType_Key: info[*ast.MapType]("Key"), + MapType_Value: info[*ast.MapType]("Value"), + ParenExpr_X: info[*ast.ParenExpr]("X"), + RangeStmt_Body: info[*ast.RangeStmt]("Body"), + RangeStmt_Key: info[*ast.RangeStmt]("Key"), + RangeStmt_Value: info[*ast.RangeStmt]("Value"), + RangeStmt_X: info[*ast.RangeStmt]("X"), + ReturnStmt_Results: info[*ast.ReturnStmt]("Results"), + SelectStmt_Body: info[*ast.SelectStmt]("Body"), + SelectorExpr_Sel: info[*ast.SelectorExpr]("Sel"), + SelectorExpr_X: info[*ast.SelectorExpr]("X"), + SendStmt_Chan: info[*ast.SendStmt]("Chan"), + SendStmt_Value: info[*ast.SendStmt]("Value"), + SliceExpr_High: info[*ast.SliceExpr]("High"), + SliceExpr_Low: info[*ast.SliceExpr]("Low"), + SliceExpr_Max: info[*ast.SliceExpr]("Max"), + SliceExpr_X: info[*ast.SliceExpr]("X"), + StarExpr_X: info[*ast.StarExpr]("X"), + StructType_Fields: info[*ast.StructType]("Fields"), + SwitchStmt_Body: info[*ast.SwitchStmt]("Body"), + SwitchStmt_Init: info[*ast.SwitchStmt]("Init"), + SwitchStmt_Tag: info[*ast.SwitchStmt]("Tag"), + TypeAssertExpr_Type: info[*ast.TypeAssertExpr]("Type"), + TypeAssertExpr_X: info[*ast.TypeAssertExpr]("X"), + TypeSpec_Comment: info[*ast.TypeSpec]("Comment"), + TypeSpec_Doc: info[*ast.TypeSpec]("Doc"), + TypeSpec_Name: info[*ast.TypeSpec]("Name"), + TypeSpec_Type: info[*ast.TypeSpec]("Type"), + TypeSpec_TypeParams: info[*ast.TypeSpec]("TypeParams"), + TypeSwitchStmt_Assign: info[*ast.TypeSwitchStmt]("Assign"), + TypeSwitchStmt_Body: info[*ast.TypeSwitchStmt]("Body"), + TypeSwitchStmt_Init: info[*ast.TypeSwitchStmt]("Init"), + UnaryExpr_X: info[*ast.UnaryExpr]("X"), + ValueSpec_Comment: info[*ast.ValueSpec]("Comment"), + ValueSpec_Doc: info[*ast.ValueSpec]("Doc"), + ValueSpec_Names: info[*ast.ValueSpec]("Names"), + ValueSpec_Type: info[*ast.ValueSpec]("Type"), + ValueSpec_Values: info[*ast.ValueSpec]("Values"), +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/fmtstr/parse.go b/src/cmd/vendor/golang.org/x/tools/internal/fmtstr/parse.go new file mode 100644 index 00000000000000..9ab264f45d6d75 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/fmtstr/parse.go @@ -0,0 +1,370 @@ +// Copyright 2024 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. + +// Package fmtstr defines a parser for format strings as used by [fmt.Printf]. +package fmtstr + +import ( + "fmt" + "strconv" + "strings" + "unicode/utf8" +) + +// Operation holds the parsed representation of a printf operation such as "%3.*[4]d". +// It is constructed by [Parse]. +type Operation struct { + Text string // full text of the operation, e.g. "%[2]*.3d" + Verb Verb // verb specifier, guaranteed to exist, e.g., 'd' in '%[1]d' + Range Range // the range of Text within the overall format string + Flags string // formatting flags, e.g. "-0" + Width Size // width specifier, e.g., '3' in '%3d' + Prec Size // precision specifier, e.g., '.4' in '%.4f' +} + +// Size describes an optional width or precision in a format operation. +// It may represent no value, a literal number, an asterisk, or an indexed asterisk. +type Size struct { + // At most one of these two fields is non-negative. + Fixed int // e.g. 4 from "%4d", otherwise -1 + Dynamic int // index of argument providing dynamic size (e.g. %*d or %[3]*d), otherwise -1 + + Index int // If the width or precision uses an indexed argument (e.g. 2 in %[2]*d), this is the index, otherwise -1 + Range Range // position of the size specifier within the operation +} + +// Verb represents the verb character of a format operation (e.g., 'd', 's', 'f'). +// It also includes positional information and any explicit argument indexing. +type Verb struct { + Verb rune + Range Range // positional range of the verb in the format string + Index int // index of an indexed argument, (e.g. 2 in %[2]d), otherwise -1 + ArgIndex int // argument index (0-based) associated with this verb, relative to CallExpr +} + +// byte offsets of format string +type Range struct { + Start, End int +} + +// Parse takes a format string and its index in the printf-like call, +// parses out all format operations, returns a slice of parsed +// [Operation] which describes flags, width, precision, verb, and argument indexing, +// or an error if parsing fails. +// +// All error messages are in predicate form ("call has a problem") +// so that they may be affixed into a subject ("log.Printf "). +// +// The flags will only be a subset of ['#', '0', '+', '-', ' ']. +// It does not perform any validation of verbs, nor the +// existence of corresponding arguments (obviously it can't). The provided format string may differ +// from the one in CallExpr, such as a concatenated string or a string +// referred to by the argument in the CallExpr. +func Parse(format string, idx int) ([]*Operation, error) { + if !strings.Contains(format, "%") { + return nil, fmt.Errorf("call has arguments but no formatting directives") + } + + firstArg := idx + 1 // Arguments are immediately after format string. + argNum := firstArg + var operations []*Operation + for i, w := 0, 0; i < len(format); i += w { + w = 1 + if format[i] != '%' { + continue + } + state, err := parseOperation(format[i:], firstArg, argNum) + if err != nil { + return nil, err + } + + state.operation.addOffset(i) + operations = append(operations, state.operation) + + w = len(state.operation.Text) + // Do not waste an argument for '%'. + if state.operation.Verb.Verb != '%' { + argNum = state.argNum + 1 + } + } + return operations, nil +} + +// Internal parsing state to operation. +type state struct { + operation *Operation + firstArg int // index of the first argument after the format string + argNum int // which argument we're expecting to format now + hasIndex bool // whether the argument is indexed + index int // the encountered index + indexPos int // the encountered index's offset + indexPending bool // whether we have an indexed argument that has not resolved + nbytes int // number of bytes of the format string consumed +} + +// parseOperation parses one format operation starting at the given substring `format`, +// which should begin with '%'. It returns a fully populated state or an error +// if the operation is malformed. The firstArg and argNum parameters help determine how +// arguments map to this operation. +// +// Parse sequence: '%' -> flags -> {[N]* or width} -> .{[N]* or precision} -> [N] -> verb. +func parseOperation(format string, firstArg, argNum int) (*state, error) { + state := &state{ + operation: &Operation{ + Text: format, + Width: Size{ + Fixed: -1, + Dynamic: -1, + Index: -1, + }, + Prec: Size{ + Fixed: -1, + Dynamic: -1, + Index: -1, + }, + }, + firstArg: firstArg, + argNum: argNum, + hasIndex: false, + index: 0, + indexPos: 0, + indexPending: false, + nbytes: len("%"), // There's guaranteed to be a percent sign. + } + // There may be flags. + state.parseFlags() + // There may be an index. + if err := state.parseIndex(); err != nil { + return nil, err + } + // There may be a width. + state.parseSize(Width) + // There may be a precision. + if err := state.parsePrecision(); err != nil { + return nil, err + } + // Now a verb, possibly prefixed by an index (which we may already have). + if !state.indexPending { + if err := state.parseIndex(); err != nil { + return nil, err + } + } + if state.nbytes == len(state.operation.Text) { + return nil, fmt.Errorf("format %s is missing verb at end of string", state.operation.Text) + } + verb, w := utf8.DecodeRuneInString(state.operation.Text[state.nbytes:]) + + // Ensure there must be a verb. + if state.indexPending { + state.operation.Verb = Verb{ + Verb: verb, + Range: Range{ + Start: state.indexPos, + End: state.nbytes + w, + }, + Index: state.index, + ArgIndex: state.argNum, + } + } else { + state.operation.Verb = Verb{ + Verb: verb, + Range: Range{ + Start: state.nbytes, + End: state.nbytes + w, + }, + Index: -1, + ArgIndex: state.argNum, + } + } + + state.nbytes += w + state.operation.Text = state.operation.Text[:state.nbytes] + return state, nil +} + +// addOffset adjusts the recorded positions in Verb, Width, Prec, and the +// operation's overall Range to be relative to the position in the full format string. +func (s *Operation) addOffset(parsedLen int) { + s.Verb.Range.Start += parsedLen + s.Verb.Range.End += parsedLen + + s.Range.Start = parsedLen + s.Range.End = s.Verb.Range.End + + // one of Fixed or Dynamic is non-negative means existence. + if s.Prec.Fixed != -1 || s.Prec.Dynamic != -1 { + s.Prec.Range.Start += parsedLen + s.Prec.Range.End += parsedLen + } + if s.Width.Fixed != -1 || s.Width.Dynamic != -1 { + s.Width.Range.Start += parsedLen + s.Width.Range.End += parsedLen + } +} + +// parseFlags accepts any printf flags. +func (s *state) parseFlags() { + s.operation.Flags = prefixOf(s.operation.Text[s.nbytes:], "#0+- ") + s.nbytes += len(s.operation.Flags) +} + +// prefixOf returns the prefix of s composed only of runes from the specified set. +func prefixOf(s, set string) string { + rest := strings.TrimLeft(s, set) + return s[:len(s)-len(rest)] +} + +// parseIndex parses an argument index of the form "[n]" that can appear +// in a printf operation (e.g., "%[2]d"). Returns an error if syntax is +// malformed or index is invalid. +func (s *state) parseIndex() error { + if s.nbytes == len(s.operation.Text) || s.operation.Text[s.nbytes] != '[' { + return nil + } + // Argument index present. + s.nbytes++ // skip '[' + start := s.nbytes + if num, ok := s.scanNum(); ok { + // Later consumed/stored by a '*' or verb. + s.index = num + s.indexPos = start - 1 + } + + ok := true + if s.nbytes == len(s.operation.Text) || s.nbytes == start || s.operation.Text[s.nbytes] != ']' { + ok = false // syntax error is either missing "]" or invalid index. + s.nbytes = strings.Index(s.operation.Text[start:], "]") + if s.nbytes < 0 { + return fmt.Errorf("format %s is missing closing ]", s.operation.Text) + } + s.nbytes = s.nbytes + start + } + arg32, err := strconv.ParseInt(s.operation.Text[start:s.nbytes], 10, 32) + if err != nil || !ok || arg32 <= 0 { + return fmt.Errorf("format has invalid argument index [%s]", s.operation.Text[start:s.nbytes]) + } + + s.nbytes++ // skip ']' + arg := int(arg32) + arg += s.firstArg - 1 // We want to zero-index the actual arguments. + s.argNum = arg + s.hasIndex = true + s.indexPending = true + return nil +} + +// scanNum advances through a decimal number if present, which represents a [Size] or [Index]. +func (s *state) scanNum() (int, bool) { + start := s.nbytes + for ; s.nbytes < len(s.operation.Text); s.nbytes++ { + c := s.operation.Text[s.nbytes] + if c < '0' || '9' < c { + if start < s.nbytes { + num, _ := strconv.ParseInt(s.operation.Text[start:s.nbytes], 10, 32) + return int(num), true + } else { + return 0, false + } + } + } + return 0, false +} + +type sizeType int + +const ( + Width sizeType = iota + Precision +) + +// parseSize parses a width or precision specifier. It handles literal numeric +// values (e.g., "%3d"), asterisk values (e.g., "%*d"), or indexed asterisk values (e.g., "%[2]*d"). +func (s *state) parseSize(kind sizeType) { + if s.nbytes < len(s.operation.Text) && s.operation.Text[s.nbytes] == '*' { + s.nbytes++ + if s.indexPending { + // Absorb it. + s.indexPending = false + size := Size{ + Fixed: -1, + Dynamic: s.argNum, + Index: s.index, + Range: Range{ + Start: s.indexPos, + End: s.nbytes, + }, + } + switch kind { + case Width: + s.operation.Width = size + case Precision: + // Include the leading '.'. + size.Range.Start -= len(".") + s.operation.Prec = size + default: + panic(kind) + } + } else { + // Non-indexed asterisk: "%*d". + size := Size{ + Dynamic: s.argNum, + Index: -1, + Fixed: -1, + Range: Range{ + Start: s.nbytes - 1, + End: s.nbytes, + }, + } + switch kind { + case Width: + s.operation.Width = size + case Precision: + // For precision, include the '.' in the range. + size.Range.Start -= 1 + s.operation.Prec = size + default: + panic(kind) + } + } + s.argNum++ + } else { // Literal number, e.g. "%10d" + start := s.nbytes + if num, ok := s.scanNum(); ok { + size := Size{ + Fixed: num, + Index: -1, + Dynamic: -1, + Range: Range{ + Start: start, + End: s.nbytes, + }, + } + switch kind { + case Width: + s.operation.Width = size + case Precision: + // Include the leading '.'. + size.Range.Start -= 1 + s.operation.Prec = size + default: + panic(kind) + } + } + } +} + +// parsePrecision checks if there's a precision specified after a '.' character. +// If found, it may also parse an index or an asterisk. Returns an error if any index +// parsing fails. +func (s *state) parsePrecision() error { + // If there's a period, there may be a precision. + if s.nbytes < len(s.operation.Text) && s.operation.Text[s.nbytes] == '.' { + s.nbytes++ + if err := s.parseIndex(); err != nil { + return err + } + s.parseSize(Precision) + } + return nil +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/stdlib/manifest.go b/src/cmd/vendor/golang.org/x/tools/internal/stdlib/manifest.go index cdaac9ab34df26..e7d0aee218648c 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/stdlib/manifest.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/stdlib/manifest.go @@ -268,6 +268,8 @@ var PackageSymbols = map[string][]Symbol{ {"ErrTooLarge", Var, 0}, {"Fields", Func, 0}, {"FieldsFunc", Func, 0}, + {"FieldsFuncSeq", Func, 24}, + {"FieldsSeq", Func, 24}, {"HasPrefix", Func, 0}, {"HasSuffix", Func, 0}, {"Index", Func, 0}, @@ -280,6 +282,7 @@ var PackageSymbols = map[string][]Symbol{ {"LastIndexAny", Func, 0}, {"LastIndexByte", Func, 5}, {"LastIndexFunc", Func, 0}, + {"Lines", Func, 24}, {"Map", Func, 0}, {"MinRead", Const, 0}, {"NewBuffer", Func, 0}, @@ -293,7 +296,9 @@ var PackageSymbols = map[string][]Symbol{ {"Split", Func, 0}, {"SplitAfter", Func, 0}, {"SplitAfterN", Func, 0}, + {"SplitAfterSeq", Func, 24}, {"SplitN", Func, 0}, + {"SplitSeq", Func, 24}, {"Title", Func, 0}, {"ToLower", Func, 0}, {"ToLowerSpecial", Func, 0}, @@ -535,6 +540,7 @@ var PackageSymbols = map[string][]Symbol{ {"NewCTR", Func, 0}, {"NewGCM", Func, 2}, {"NewGCMWithNonceSize", Func, 5}, + {"NewGCMWithRandomNonce", Func, 24}, {"NewGCMWithTagSize", Func, 11}, {"NewOFB", Func, 0}, {"Stream", Type, 0}, @@ -673,6 +679,14 @@ var PackageSymbols = map[string][]Symbol{ {"Unmarshal", Func, 0}, {"UnmarshalCompressed", Func, 15}, }, + "crypto/fips140": { + {"Enabled", Func, 24}, + }, + "crypto/hkdf": { + {"Expand", Func, 24}, + {"Extract", Func, 24}, + {"Key", Func, 24}, + }, "crypto/hmac": { {"Equal", Func, 1}, {"New", Func, 0}, @@ -683,11 +697,43 @@ var PackageSymbols = map[string][]Symbol{ {"Size", Const, 0}, {"Sum", Func, 2}, }, + "crypto/mlkem": { + {"(*DecapsulationKey1024).Bytes", Method, 24}, + {"(*DecapsulationKey1024).Decapsulate", Method, 24}, + {"(*DecapsulationKey1024).EncapsulationKey", Method, 24}, + {"(*DecapsulationKey768).Bytes", Method, 24}, + {"(*DecapsulationKey768).Decapsulate", Method, 24}, + {"(*DecapsulationKey768).EncapsulationKey", Method, 24}, + {"(*EncapsulationKey1024).Bytes", Method, 24}, + {"(*EncapsulationKey1024).Encapsulate", Method, 24}, + {"(*EncapsulationKey768).Bytes", Method, 24}, + {"(*EncapsulationKey768).Encapsulate", Method, 24}, + {"CiphertextSize1024", Const, 24}, + {"CiphertextSize768", Const, 24}, + {"DecapsulationKey1024", Type, 24}, + {"DecapsulationKey768", Type, 24}, + {"EncapsulationKey1024", Type, 24}, + {"EncapsulationKey768", Type, 24}, + {"EncapsulationKeySize1024", Const, 24}, + {"EncapsulationKeySize768", Const, 24}, + {"GenerateKey1024", Func, 24}, + {"GenerateKey768", Func, 24}, + {"NewDecapsulationKey1024", Func, 24}, + {"NewDecapsulationKey768", Func, 24}, + {"NewEncapsulationKey1024", Func, 24}, + {"NewEncapsulationKey768", Func, 24}, + {"SeedSize", Const, 24}, + {"SharedKeySize", Const, 24}, + }, + "crypto/pbkdf2": { + {"Key", Func, 24}, + }, "crypto/rand": { {"Int", Func, 0}, {"Prime", Func, 0}, {"Read", Func, 0}, {"Reader", Var, 0}, + {"Text", Func, 24}, }, "crypto/rc4": { {"(*Cipher).Reset", Method, 0}, @@ -766,6 +812,39 @@ var PackageSymbols = map[string][]Symbol{ {"Sum224", Func, 2}, {"Sum256", Func, 2}, }, + "crypto/sha3": { + {"(*SHA3).AppendBinary", Method, 24}, + {"(*SHA3).BlockSize", Method, 24}, + {"(*SHA3).MarshalBinary", Method, 24}, + {"(*SHA3).Reset", Method, 24}, + {"(*SHA3).Size", Method, 24}, + {"(*SHA3).Sum", Method, 24}, + {"(*SHA3).UnmarshalBinary", Method, 24}, + {"(*SHA3).Write", Method, 24}, + {"(*SHAKE).AppendBinary", Method, 24}, + {"(*SHAKE).BlockSize", Method, 24}, + {"(*SHAKE).MarshalBinary", Method, 24}, + {"(*SHAKE).Read", Method, 24}, + {"(*SHAKE).Reset", Method, 24}, + {"(*SHAKE).UnmarshalBinary", Method, 24}, + {"(*SHAKE).Write", Method, 24}, + {"New224", Func, 24}, + {"New256", Func, 24}, + {"New384", Func, 24}, + {"New512", Func, 24}, + {"NewCSHAKE128", Func, 24}, + {"NewCSHAKE256", Func, 24}, + {"NewSHAKE128", Func, 24}, + {"NewSHAKE256", Func, 24}, + {"SHA3", Type, 24}, + {"SHAKE", Type, 24}, + {"Sum224", Func, 24}, + {"Sum256", Func, 24}, + {"Sum384", Func, 24}, + {"Sum512", Func, 24}, + {"SumSHAKE128", Func, 24}, + {"SumSHAKE256", Func, 24}, + }, "crypto/sha512": { {"BlockSize", Const, 0}, {"New", Func, 0}, @@ -788,6 +867,7 @@ var PackageSymbols = map[string][]Symbol{ {"ConstantTimeEq", Func, 0}, {"ConstantTimeLessOrEq", Func, 2}, {"ConstantTimeSelect", Func, 0}, + {"WithDataIndependentTiming", Func, 24}, {"XORBytes", Func, 20}, }, "crypto/tls": { @@ -864,6 +944,7 @@ var PackageSymbols = map[string][]Symbol{ {"ClientHelloInfo", Type, 4}, {"ClientHelloInfo.CipherSuites", Field, 4}, {"ClientHelloInfo.Conn", Field, 8}, + {"ClientHelloInfo.Extensions", Field, 24}, {"ClientHelloInfo.ServerName", Field, 4}, {"ClientHelloInfo.SignatureSchemes", Field, 8}, {"ClientHelloInfo.SupportedCurves", Field, 4}, @@ -881,6 +962,7 @@ var PackageSymbols = map[string][]Symbol{ {"Config.CurvePreferences", Field, 3}, {"Config.DynamicRecordSizingDisabled", Field, 7}, {"Config.EncryptedClientHelloConfigList", Field, 23}, + {"Config.EncryptedClientHelloKeys", Field, 24}, {"Config.EncryptedClientHelloRejectionVerify", Field, 23}, {"Config.GetCertificate", Field, 4}, {"Config.GetClientCertificate", Field, 8}, @@ -934,6 +1016,10 @@ var PackageSymbols = map[string][]Symbol{ {"ECHRejectionError", Type, 23}, {"ECHRejectionError.RetryConfigList", Field, 23}, {"Ed25519", Const, 13}, + {"EncryptedClientHelloKey", Type, 24}, + {"EncryptedClientHelloKey.Config", Field, 24}, + {"EncryptedClientHelloKey.PrivateKey", Field, 24}, + {"EncryptedClientHelloKey.SendAsRetry", Field, 24}, {"InsecureCipherSuites", Func, 14}, {"Listen", Func, 0}, {"LoadX509KeyPair", Func, 0}, @@ -1032,6 +1118,7 @@ var PackageSymbols = map[string][]Symbol{ {"VersionTLS12", Const, 2}, {"VersionTLS13", Const, 12}, {"X25519", Const, 8}, + {"X25519MLKEM768", Const, 24}, {"X509KeyPair", Func, 0}, }, "crypto/x509": { @@ -1056,6 +1143,8 @@ var PackageSymbols = map[string][]Symbol{ {"(ConstraintViolationError).Error", Method, 0}, {"(HostnameError).Error", Method, 0}, {"(InsecureAlgorithmError).Error", Method, 6}, + {"(OID).AppendBinary", Method, 24}, + {"(OID).AppendText", Method, 24}, {"(OID).Equal", Method, 22}, {"(OID).EqualASN1OID", Method, 22}, {"(OID).MarshalBinary", Method, 23}, @@ -1084,6 +1173,10 @@ var PackageSymbols = map[string][]Symbol{ {"Certificate.Extensions", Field, 2}, {"Certificate.ExtraExtensions", Field, 2}, {"Certificate.IPAddresses", Field, 1}, + {"Certificate.InhibitAnyPolicy", Field, 24}, + {"Certificate.InhibitAnyPolicyZero", Field, 24}, + {"Certificate.InhibitPolicyMapping", Field, 24}, + {"Certificate.InhibitPolicyMappingZero", Field, 24}, {"Certificate.IsCA", Field, 0}, {"Certificate.Issuer", Field, 0}, {"Certificate.IssuingCertificateURL", Field, 2}, @@ -1100,6 +1193,7 @@ var PackageSymbols = map[string][]Symbol{ {"Certificate.PermittedURIDomains", Field, 10}, {"Certificate.Policies", Field, 22}, {"Certificate.PolicyIdentifiers", Field, 0}, + {"Certificate.PolicyMappings", Field, 24}, {"Certificate.PublicKey", Field, 0}, {"Certificate.PublicKeyAlgorithm", Field, 0}, {"Certificate.Raw", Field, 0}, @@ -1107,6 +1201,8 @@ var PackageSymbols = map[string][]Symbol{ {"Certificate.RawSubject", Field, 0}, {"Certificate.RawSubjectPublicKeyInfo", Field, 0}, {"Certificate.RawTBSCertificate", Field, 0}, + {"Certificate.RequireExplicitPolicy", Field, 24}, + {"Certificate.RequireExplicitPolicyZero", Field, 24}, {"Certificate.SerialNumber", Field, 0}, {"Certificate.Signature", Field, 0}, {"Certificate.SignatureAlgorithm", Field, 0}, @@ -1198,6 +1294,7 @@ var PackageSymbols = map[string][]Symbol{ {"NameConstraintsWithoutSANs", Const, 10}, {"NameMismatch", Const, 8}, {"NewCertPool", Func, 0}, + {"NoValidChains", Const, 24}, {"NotAuthorizedToSign", Const, 0}, {"OID", Type, 22}, {"OIDFromInts", Func, 22}, @@ -1219,6 +1316,9 @@ var PackageSymbols = map[string][]Symbol{ {"ParsePKCS8PrivateKey", Func, 0}, {"ParsePKIXPublicKey", Func, 0}, {"ParseRevocationList", Func, 19}, + {"PolicyMapping", Type, 24}, + {"PolicyMapping.IssuerDomainPolicy", Field, 24}, + {"PolicyMapping.SubjectDomainPolicy", Field, 24}, {"PublicKeyAlgorithm", Type, 0}, {"PureEd25519", Const, 13}, {"RSA", Const, 0}, @@ -1265,6 +1365,7 @@ var PackageSymbols = map[string][]Symbol{ {"UnknownPublicKeyAlgorithm", Const, 0}, {"UnknownSignatureAlgorithm", Const, 0}, {"VerifyOptions", Type, 0}, + {"VerifyOptions.CertificatePolicies", Field, 24}, {"VerifyOptions.CurrentTime", Field, 0}, {"VerifyOptions.DNSName", Field, 0}, {"VerifyOptions.Intermediates", Field, 0}, @@ -1975,6 +2076,8 @@ var PackageSymbols = map[string][]Symbol{ {"(*File).DynString", Method, 1}, {"(*File).DynValue", Method, 21}, {"(*File).DynamicSymbols", Method, 4}, + {"(*File).DynamicVersionNeeds", Method, 24}, + {"(*File).DynamicVersions", Method, 24}, {"(*File).ImportedLibraries", Method, 0}, {"(*File).ImportedSymbols", Method, 0}, {"(*File).Section", Method, 0}, @@ -2048,6 +2151,8 @@ var PackageSymbols = map[string][]Symbol{ {"(Type).String", Method, 0}, {"(Version).GoString", Method, 0}, {"(Version).String", Method, 0}, + {"(VersionIndex).Index", Method, 24}, + {"(VersionIndex).IsHidden", Method, 24}, {"ARM_MAGIC_TRAMP_NUMBER", Const, 0}, {"COMPRESS_HIOS", Const, 6}, {"COMPRESS_HIPROC", Const, 6}, @@ -2240,6 +2345,19 @@ var PackageSymbols = map[string][]Symbol{ {"DynFlag", Type, 0}, {"DynFlag1", Type, 21}, {"DynTag", Type, 0}, + {"DynamicVersion", Type, 24}, + {"DynamicVersion.Deps", Field, 24}, + {"DynamicVersion.Flags", Field, 24}, + {"DynamicVersion.Index", Field, 24}, + {"DynamicVersion.Name", Field, 24}, + {"DynamicVersionDep", Type, 24}, + {"DynamicVersionDep.Dep", Field, 24}, + {"DynamicVersionDep.Flags", Field, 24}, + {"DynamicVersionDep.Index", Field, 24}, + {"DynamicVersionFlag", Type, 24}, + {"DynamicVersionNeed", Type, 24}, + {"DynamicVersionNeed.Name", Field, 24}, + {"DynamicVersionNeed.Needs", Field, 24}, {"EI_ABIVERSION", Const, 0}, {"EI_CLASS", Const, 0}, {"EI_DATA", Const, 0}, @@ -3718,6 +3836,7 @@ var PackageSymbols = map[string][]Symbol{ {"SymType", Type, 0}, {"SymVis", Type, 0}, {"Symbol", Type, 0}, + {"Symbol.HasVersion", Field, 24}, {"Symbol.Info", Field, 0}, {"Symbol.Library", Field, 13}, {"Symbol.Name", Field, 0}, @@ -3726,8 +3845,13 @@ var PackageSymbols = map[string][]Symbol{ {"Symbol.Size", Field, 0}, {"Symbol.Value", Field, 0}, {"Symbol.Version", Field, 13}, + {"Symbol.VersionIndex", Field, 24}, {"Type", Type, 0}, + {"VER_FLG_BASE", Const, 24}, + {"VER_FLG_INFO", Const, 24}, + {"VER_FLG_WEAK", Const, 24}, {"Version", Type, 0}, + {"VersionIndex", Type, 24}, }, "debug/gosym": { {"(*DecodingError).Error", Method, 0}, @@ -4453,8 +4577,10 @@ var PackageSymbols = map[string][]Symbol{ {"FS", Type, 16}, }, "encoding": { + {"BinaryAppender", Type, 24}, {"BinaryMarshaler", Type, 2}, {"BinaryUnmarshaler", Type, 2}, + {"TextAppender", Type, 24}, {"TextMarshaler", Type, 2}, {"TextUnmarshaler", Type, 2}, }, @@ -5984,13 +6110,16 @@ var PackageSymbols = map[string][]Symbol{ {"(*Interface).Complete", Method, 5}, {"(*Interface).Embedded", Method, 5}, {"(*Interface).EmbeddedType", Method, 11}, + {"(*Interface).EmbeddedTypes", Method, 24}, {"(*Interface).Empty", Method, 5}, {"(*Interface).ExplicitMethod", Method, 5}, + {"(*Interface).ExplicitMethods", Method, 24}, {"(*Interface).IsComparable", Method, 18}, {"(*Interface).IsImplicit", Method, 18}, {"(*Interface).IsMethodSet", Method, 18}, {"(*Interface).MarkImplicit", Method, 18}, {"(*Interface).Method", Method, 5}, + {"(*Interface).Methods", Method, 24}, {"(*Interface).NumEmbeddeds", Method, 5}, {"(*Interface).NumExplicitMethods", Method, 5}, {"(*Interface).NumMethods", Method, 5}, @@ -6011,9 +6140,11 @@ var PackageSymbols = map[string][]Symbol{ {"(*MethodSet).At", Method, 5}, {"(*MethodSet).Len", Method, 5}, {"(*MethodSet).Lookup", Method, 5}, + {"(*MethodSet).Methods", Method, 24}, {"(*MethodSet).String", Method, 5}, {"(*Named).AddMethod", Method, 5}, {"(*Named).Method", Method, 5}, + {"(*Named).Methods", Method, 24}, {"(*Named).NumMethods", Method, 5}, {"(*Named).Obj", Method, 5}, {"(*Named).Origin", Method, 18}, @@ -6054,6 +6185,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Pointer).String", Method, 5}, {"(*Pointer).Underlying", Method, 5}, {"(*Scope).Child", Method, 5}, + {"(*Scope).Children", Method, 24}, {"(*Scope).Contains", Method, 5}, {"(*Scope).End", Method, 5}, {"(*Scope).Innermost", Method, 5}, @@ -6089,6 +6221,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*StdSizes).Offsetsof", Method, 5}, {"(*StdSizes).Sizeof", Method, 5}, {"(*Struct).Field", Method, 5}, + {"(*Struct).Fields", Method, 24}, {"(*Struct).NumFields", Method, 5}, {"(*Struct).String", Method, 5}, {"(*Struct).Tag", Method, 5}, @@ -6100,8 +6233,10 @@ var PackageSymbols = map[string][]Symbol{ {"(*Tuple).Len", Method, 5}, {"(*Tuple).String", Method, 5}, {"(*Tuple).Underlying", Method, 5}, + {"(*Tuple).Variables", Method, 24}, {"(*TypeList).At", Method, 18}, {"(*TypeList).Len", Method, 18}, + {"(*TypeList).Types", Method, 24}, {"(*TypeName).Exported", Method, 5}, {"(*TypeName).Id", Method, 5}, {"(*TypeName).IsAlias", Method, 9}, @@ -6119,9 +6254,11 @@ var PackageSymbols = map[string][]Symbol{ {"(*TypeParam).Underlying", Method, 18}, {"(*TypeParamList).At", Method, 18}, {"(*TypeParamList).Len", Method, 18}, + {"(*TypeParamList).TypeParams", Method, 24}, {"(*Union).Len", Method, 18}, {"(*Union).String", Method, 18}, {"(*Union).Term", Method, 18}, + {"(*Union).Terms", Method, 24}, {"(*Union).Underlying", Method, 18}, {"(*Var).Anonymous", Method, 5}, {"(*Var).Embedded", Method, 11}, @@ -6392,10 +6529,12 @@ var PackageSymbols = map[string][]Symbol{ {"(*Hash).WriteByte", Method, 14}, {"(*Hash).WriteString", Method, 14}, {"Bytes", Func, 19}, + {"Comparable", Func, 24}, {"Hash", Type, 14}, {"MakeSeed", Func, 14}, {"Seed", Type, 14}, {"String", Func, 19}, + {"WriteComparable", Func, 24}, }, "html": { {"EscapeString", Func, 0}, @@ -7082,6 +7221,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*JSONHandler).WithGroup", Method, 21}, {"(*Level).UnmarshalJSON", Method, 21}, {"(*Level).UnmarshalText", Method, 21}, + {"(*LevelVar).AppendText", Method, 24}, {"(*LevelVar).Level", Method, 21}, {"(*LevelVar).MarshalText", Method, 21}, {"(*LevelVar).Set", Method, 21}, @@ -7110,6 +7250,7 @@ var PackageSymbols = map[string][]Symbol{ {"(Attr).Equal", Method, 21}, {"(Attr).String", Method, 21}, {"(Kind).String", Method, 21}, + {"(Level).AppendText", Method, 24}, {"(Level).Level", Method, 21}, {"(Level).MarshalJSON", Method, 21}, {"(Level).MarshalText", Method, 21}, @@ -7140,6 +7281,7 @@ var PackageSymbols = map[string][]Symbol{ {"Debug", Func, 21}, {"DebugContext", Func, 21}, {"Default", Func, 21}, + {"DiscardHandler", Var, 24}, {"Duration", Func, 21}, {"DurationValue", Func, 21}, {"Error", Func, 21}, @@ -7375,6 +7517,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Float).Acc", Method, 5}, {"(*Float).Add", Method, 5}, {"(*Float).Append", Method, 5}, + {"(*Float).AppendText", Method, 24}, {"(*Float).Cmp", Method, 5}, {"(*Float).Copy", Method, 5}, {"(*Float).Float32", Method, 5}, @@ -7421,6 +7564,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Int).And", Method, 0}, {"(*Int).AndNot", Method, 0}, {"(*Int).Append", Method, 6}, + {"(*Int).AppendText", Method, 24}, {"(*Int).Binomial", Method, 0}, {"(*Int).Bit", Method, 0}, {"(*Int).BitLen", Method, 0}, @@ -7477,6 +7621,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Int).Xor", Method, 0}, {"(*Rat).Abs", Method, 0}, {"(*Rat).Add", Method, 0}, + {"(*Rat).AppendText", Method, 24}, {"(*Rat).Cmp", Method, 0}, {"(*Rat).Denom", Method, 0}, {"(*Rat).Float32", Method, 4}, @@ -7659,11 +7804,13 @@ var PackageSymbols = map[string][]Symbol{ {"Zipf", Type, 0}, }, "math/rand/v2": { + {"(*ChaCha8).AppendBinary", Method, 24}, {"(*ChaCha8).MarshalBinary", Method, 22}, {"(*ChaCha8).Read", Method, 23}, {"(*ChaCha8).Seed", Method, 22}, {"(*ChaCha8).Uint64", Method, 22}, {"(*ChaCha8).UnmarshalBinary", Method, 22}, + {"(*PCG).AppendBinary", Method, 24}, {"(*PCG).MarshalBinary", Method, 22}, {"(*PCG).Seed", Method, 22}, {"(*PCG).Uint64", Method, 22}, @@ -7931,6 +8078,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*UnixListener).SyscallConn", Method, 10}, {"(Flags).String", Method, 0}, {"(HardwareAddr).String", Method, 0}, + {"(IP).AppendText", Method, 24}, {"(IP).DefaultMask", Method, 0}, {"(IP).Equal", Method, 0}, {"(IP).IsGlobalUnicast", Method, 0}, @@ -8131,6 +8279,9 @@ var PackageSymbols = map[string][]Symbol{ {"(*MaxBytesError).Error", Method, 19}, {"(*ProtocolError).Error", Method, 0}, {"(*ProtocolError).Is", Method, 21}, + {"(*Protocols).SetHTTP1", Method, 24}, + {"(*Protocols).SetHTTP2", Method, 24}, + {"(*Protocols).SetUnencryptedHTTP2", Method, 24}, {"(*Request).AddCookie", Method, 0}, {"(*Request).BasicAuth", Method, 4}, {"(*Request).Clone", Method, 13}, @@ -8190,6 +8341,10 @@ var PackageSymbols = map[string][]Symbol{ {"(Header).Values", Method, 14}, {"(Header).Write", Method, 0}, {"(Header).WriteSubset", Method, 0}, + {"(Protocols).HTTP1", Method, 24}, + {"(Protocols).HTTP2", Method, 24}, + {"(Protocols).String", Method, 24}, + {"(Protocols).UnencryptedHTTP2", Method, 24}, {"AllowQuerySemicolons", Func, 17}, {"CanonicalHeaderKey", Func, 0}, {"Client", Type, 0}, @@ -8252,6 +8407,18 @@ var PackageSymbols = map[string][]Symbol{ {"FileSystem", Type, 0}, {"Flusher", Type, 0}, {"Get", Func, 0}, + {"HTTP2Config", Type, 24}, + {"HTTP2Config.CountError", Field, 24}, + {"HTTP2Config.MaxConcurrentStreams", Field, 24}, + {"HTTP2Config.MaxDecoderHeaderTableSize", Field, 24}, + {"HTTP2Config.MaxEncoderHeaderTableSize", Field, 24}, + {"HTTP2Config.MaxReadFrameSize", Field, 24}, + {"HTTP2Config.MaxReceiveBufferPerConnection", Field, 24}, + {"HTTP2Config.MaxReceiveBufferPerStream", Field, 24}, + {"HTTP2Config.PermitProhibitedCipherSuites", Field, 24}, + {"HTTP2Config.PingTimeout", Field, 24}, + {"HTTP2Config.SendPingTimeout", Field, 24}, + {"HTTP2Config.WriteByteTimeout", Field, 24}, {"Handle", Func, 0}, {"HandleFunc", Func, 0}, {"Handler", Type, 0}, @@ -8292,6 +8459,7 @@ var PackageSymbols = map[string][]Symbol{ {"PostForm", Func, 0}, {"ProtocolError", Type, 0}, {"ProtocolError.ErrorString", Field, 0}, + {"Protocols", Type, 24}, {"ProxyFromEnvironment", Func, 0}, {"ProxyURL", Func, 0}, {"PushOptions", Type, 8}, @@ -8361,9 +8529,11 @@ var PackageSymbols = map[string][]Symbol{ {"Server.ConnState", Field, 3}, {"Server.DisableGeneralOptionsHandler", Field, 20}, {"Server.ErrorLog", Field, 3}, + {"Server.HTTP2", Field, 24}, {"Server.Handler", Field, 0}, {"Server.IdleTimeout", Field, 8}, {"Server.MaxHeaderBytes", Field, 0}, + {"Server.Protocols", Field, 24}, {"Server.ReadHeaderTimeout", Field, 8}, {"Server.ReadTimeout", Field, 0}, {"Server.TLSConfig", Field, 0}, @@ -8453,12 +8623,14 @@ var PackageSymbols = map[string][]Symbol{ {"Transport.ExpectContinueTimeout", Field, 6}, {"Transport.ForceAttemptHTTP2", Field, 13}, {"Transport.GetProxyConnectHeader", Field, 16}, + {"Transport.HTTP2", Field, 24}, {"Transport.IdleConnTimeout", Field, 7}, {"Transport.MaxConnsPerHost", Field, 11}, {"Transport.MaxIdleConns", Field, 7}, {"Transport.MaxIdleConnsPerHost", Field, 0}, {"Transport.MaxResponseHeaderBytes", Field, 7}, {"Transport.OnProxyConnectResponse", Field, 20}, + {"Transport.Protocols", Field, 24}, {"Transport.Proxy", Field, 0}, {"Transport.ProxyConnectHeader", Field, 8}, {"Transport.ReadBufferSize", Field, 13}, @@ -8646,6 +8818,8 @@ var PackageSymbols = map[string][]Symbol{ {"(*AddrPort).UnmarshalText", Method, 18}, {"(*Prefix).UnmarshalBinary", Method, 18}, {"(*Prefix).UnmarshalText", Method, 18}, + {"(Addr).AppendBinary", Method, 24}, + {"(Addr).AppendText", Method, 24}, {"(Addr).AppendTo", Method, 18}, {"(Addr).As16", Method, 18}, {"(Addr).As4", Method, 18}, @@ -8676,6 +8850,8 @@ var PackageSymbols = map[string][]Symbol{ {"(Addr).WithZone", Method, 18}, {"(Addr).Zone", Method, 18}, {"(AddrPort).Addr", Method, 18}, + {"(AddrPort).AppendBinary", Method, 24}, + {"(AddrPort).AppendText", Method, 24}, {"(AddrPort).AppendTo", Method, 18}, {"(AddrPort).Compare", Method, 22}, {"(AddrPort).IsValid", Method, 18}, @@ -8684,6 +8860,8 @@ var PackageSymbols = map[string][]Symbol{ {"(AddrPort).Port", Method, 18}, {"(AddrPort).String", Method, 18}, {"(Prefix).Addr", Method, 18}, + {"(Prefix).AppendBinary", Method, 24}, + {"(Prefix).AppendText", Method, 24}, {"(Prefix).AppendTo", Method, 18}, {"(Prefix).Bits", Method, 18}, {"(Prefix).Contains", Method, 18}, @@ -8868,6 +9046,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Error).Temporary", Method, 6}, {"(*Error).Timeout", Method, 6}, {"(*Error).Unwrap", Method, 13}, + {"(*URL).AppendBinary", Method, 24}, {"(*URL).EscapedFragment", Method, 15}, {"(*URL).EscapedPath", Method, 5}, {"(*URL).Hostname", Method, 8}, @@ -8967,6 +9146,17 @@ var PackageSymbols = map[string][]Symbol{ {"(*ProcessState).SysUsage", Method, 0}, {"(*ProcessState).SystemTime", Method, 0}, {"(*ProcessState).UserTime", Method, 0}, + {"(*Root).Close", Method, 24}, + {"(*Root).Create", Method, 24}, + {"(*Root).FS", Method, 24}, + {"(*Root).Lstat", Method, 24}, + {"(*Root).Mkdir", Method, 24}, + {"(*Root).Name", Method, 24}, + {"(*Root).Open", Method, 24}, + {"(*Root).OpenFile", Method, 24}, + {"(*Root).OpenRoot", Method, 24}, + {"(*Root).Remove", Method, 24}, + {"(*Root).Stat", Method, 24}, {"(*SyscallError).Error", Method, 0}, {"(*SyscallError).Timeout", Method, 10}, {"(*SyscallError).Unwrap", Method, 13}, @@ -9060,6 +9250,8 @@ var PackageSymbols = map[string][]Symbol{ {"O_WRONLY", Const, 0}, {"Open", Func, 0}, {"OpenFile", Func, 0}, + {"OpenInRoot", Func, 24}, + {"OpenRoot", Func, 24}, {"PathError", Type, 0}, {"PathError.Err", Field, 0}, {"PathError.Op", Field, 0}, @@ -9081,6 +9273,7 @@ var PackageSymbols = map[string][]Symbol{ {"Remove", Func, 0}, {"RemoveAll", Func, 0}, {"Rename", Func, 0}, + {"Root", Type, 24}, {"SEEK_CUR", Const, 0}, {"SEEK_END", Const, 0}, {"SEEK_SET", Const, 0}, @@ -9422,6 +9615,7 @@ var PackageSymbols = map[string][]Symbol{ {"Zero", Func, 0}, }, "regexp": { + {"(*Regexp).AppendText", Method, 24}, {"(*Regexp).Copy", Method, 6}, {"(*Regexp).Expand", Method, 0}, {"(*Regexp).ExpandString", Method, 0}, @@ -9602,6 +9796,8 @@ var PackageSymbols = map[string][]Symbol{ {"(*StackRecord).Stack", Method, 0}, {"(*TypeAssertionError).Error", Method, 0}, {"(*TypeAssertionError).RuntimeError", Method, 0}, + {"(Cleanup).Stop", Method, 24}, + {"AddCleanup", Func, 24}, {"BlockProfile", Func, 1}, {"BlockProfileRecord", Type, 1}, {"BlockProfileRecord.Count", Field, 1}, @@ -9612,6 +9808,7 @@ var PackageSymbols = map[string][]Symbol{ {"Caller", Func, 0}, {"Callers", Func, 0}, {"CallersFrames", Func, 7}, + {"Cleanup", Type, 24}, {"Compiler", Const, 0}, {"Error", Type, 0}, {"Frame", Type, 7}, @@ -9974,6 +10171,8 @@ var PackageSymbols = map[string][]Symbol{ {"EqualFold", Func, 0}, {"Fields", Func, 0}, {"FieldsFunc", Func, 0}, + {"FieldsFuncSeq", Func, 24}, + {"FieldsSeq", Func, 24}, {"HasPrefix", Func, 0}, {"HasSuffix", Func, 0}, {"Index", Func, 0}, @@ -9986,6 +10185,7 @@ var PackageSymbols = map[string][]Symbol{ {"LastIndexAny", Func, 0}, {"LastIndexByte", Func, 5}, {"LastIndexFunc", Func, 0}, + {"Lines", Func, 24}, {"Map", Func, 0}, {"NewReader", Func, 0}, {"NewReplacer", Func, 0}, @@ -9997,7 +10197,9 @@ var PackageSymbols = map[string][]Symbol{ {"Split", Func, 0}, {"SplitAfter", Func, 0}, {"SplitAfterN", Func, 0}, + {"SplitAfterSeq", Func, 24}, {"SplitN", Func, 0}, + {"SplitSeq", Func, 24}, {"Title", Func, 0}, {"ToLower", Func, 0}, {"ToLowerSpecial", Func, 0}, @@ -16413,7 +16615,9 @@ var PackageSymbols = map[string][]Symbol{ {"ValueOf", Func, 0}, }, "testing": { + {"(*B).Chdir", Method, 24}, {"(*B).Cleanup", Method, 14}, + {"(*B).Context", Method, 24}, {"(*B).Elapsed", Method, 20}, {"(*B).Error", Method, 0}, {"(*B).Errorf", Method, 0}, @@ -16425,6 +16629,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*B).Helper", Method, 9}, {"(*B).Log", Method, 0}, {"(*B).Logf", Method, 0}, + {"(*B).Loop", Method, 24}, {"(*B).Name", Method, 8}, {"(*B).ReportAllocs", Method, 1}, {"(*B).ReportMetric", Method, 13}, @@ -16442,7 +16647,9 @@ var PackageSymbols = map[string][]Symbol{ {"(*B).StopTimer", Method, 0}, {"(*B).TempDir", Method, 15}, {"(*F).Add", Method, 18}, + {"(*F).Chdir", Method, 24}, {"(*F).Cleanup", Method, 18}, + {"(*F).Context", Method, 24}, {"(*F).Error", Method, 18}, {"(*F).Errorf", Method, 18}, {"(*F).Fail", Method, 18}, @@ -16463,7 +16670,9 @@ var PackageSymbols = map[string][]Symbol{ {"(*F).TempDir", Method, 18}, {"(*M).Run", Method, 4}, {"(*PB).Next", Method, 3}, + {"(*T).Chdir", Method, 24}, {"(*T).Cleanup", Method, 14}, + {"(*T).Context", Method, 24}, {"(*T).Deadline", Method, 15}, {"(*T).Error", Method, 0}, {"(*T).Errorf", Method, 0}, @@ -16954,7 +17163,9 @@ var PackageSymbols = map[string][]Symbol{ {"(Time).Add", Method, 0}, {"(Time).AddDate", Method, 0}, {"(Time).After", Method, 0}, + {"(Time).AppendBinary", Method, 24}, {"(Time).AppendFormat", Method, 5}, + {"(Time).AppendText", Method, 24}, {"(Time).Before", Method, 0}, {"(Time).Clock", Method, 0}, {"(Time).Compare", Method, 20}, @@ -17428,4 +17639,9 @@ var PackageSymbols = map[string][]Symbol{ {"String", Func, 0}, {"StringData", Func, 0}, }, + "weak": { + {"(Pointer).Value", Method, 24}, + {"Make", Func, 24}, + {"Pointer", Type, 24}, + }, } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go index 0b84acc5c7fa43..cdae2b8e818485 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/common.go @@ -66,75 +66,3 @@ func IsTypeParam(t types.Type) bool { _, ok := types.Unalias(t).(*types.TypeParam) return ok } - -// GenericAssignableTo is a generalization of types.AssignableTo that -// implements the following rule for uninstantiated generic types: -// -// If V and T are generic named types, then V is considered assignable to T if, -// for every possible instantiation of V[A_1, ..., A_N], the instantiation -// T[A_1, ..., A_N] is valid and V[A_1, ..., A_N] implements T[A_1, ..., A_N]. -// -// If T has structural constraints, they must be satisfied by V. -// -// For example, consider the following type declarations: -// -// type Interface[T any] interface { -// Accept(T) -// } -// -// type Container[T any] struct { -// Element T -// } -// -// func (c Container[T]) Accept(t T) { c.Element = t } -// -// In this case, GenericAssignableTo reports that instantiations of Container -// are assignable to the corresponding instantiation of Interface. -func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool { - V = types.Unalias(V) - T = types.Unalias(T) - - // If V and T are not both named, or do not have matching non-empty type - // parameter lists, fall back on types.AssignableTo. - - VN, Vnamed := V.(*types.Named) - TN, Tnamed := T.(*types.Named) - if !Vnamed || !Tnamed { - return types.AssignableTo(V, T) - } - - vtparams := VN.TypeParams() - ttparams := TN.TypeParams() - if vtparams.Len() == 0 || vtparams.Len() != ttparams.Len() || VN.TypeArgs().Len() != 0 || TN.TypeArgs().Len() != 0 { - return types.AssignableTo(V, T) - } - - // V and T have the same (non-zero) number of type params. Instantiate both - // with the type parameters of V. This must always succeed for V, and will - // succeed for T if and only if the type set of each type parameter of V is a - // subset of the type set of the corresponding type parameter of T, meaning - // that every instantiation of V corresponds to a valid instantiation of T. - - // Minor optimization: ensure we share a context across the two - // instantiations below. - if ctxt == nil { - ctxt = types.NewContext() - } - - var targs []types.Type - for i := 0; i < vtparams.Len(); i++ { - targs = append(targs, vtparams.At(i)) - } - - vinst, err := types.Instantiate(ctxt, V, targs, true) - if err != nil { - panic("type parameters should satisfy their own constraints") - } - - tinst, err := types.Instantiate(ctxt, T, targs, true) - if err != nil { - return false - } - - return types.AssignableTo(vinst, tinst) -} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go index 6e83c6fb1a2b08..27a2b179299681 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typeparams/coretype.go @@ -109,8 +109,13 @@ func CoreType(T types.Type) types.Type { // // NormalTerms makes no guarantees about the order of terms, except that it // is deterministic. -func NormalTerms(typ types.Type) ([]*types.Term, error) { - switch typ := typ.Underlying().(type) { +func NormalTerms(T types.Type) ([]*types.Term, error) { + // typeSetOf(T) == typeSetOf(Unalias(T)) + typ := types.Unalias(T) + if named, ok := typ.(*types.Named); ok { + typ = named.Underlying() + } + switch typ := typ.(type) { case *types.TypeParam: return StructuralTerms(typ) case *types.Union: @@ -118,7 +123,7 @@ func NormalTerms(typ types.Type) ([]*types.Term, error) { case *types.Interface: return InterfaceTermSet(typ) default: - return []*types.Term{types.NewTerm(false, typ)}, nil + return []*types.Term{types.NewTerm(false, T)}, nil } } diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go index 131caab2847ecf..235a6defc4c733 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/errorcode.go @@ -966,7 +966,7 @@ const ( // var _ = string(x) InvalidConversion - // InvalidUntypedConversion occurs when an there is no valid implicit + // InvalidUntypedConversion occurs when there is no valid implicit // conversion from an untyped value satisfying the type constraints of the // context in which it is used. // diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/qualifier.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/qualifier.go new file mode 100644 index 00000000000000..b64f714eb30f6e --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/qualifier.go @@ -0,0 +1,46 @@ +// Copyright 2024 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. + +package typesinternal + +import ( + "go/ast" + "go/types" + "strconv" +) + +// FileQualifier returns a [types.Qualifier] function that qualifies +// imported symbols appropriately based on the import environment of a given +// file. +// If the same package is imported multiple times, the last appearance is +// recorded. +func FileQualifier(f *ast.File, pkg *types.Package) types.Qualifier { + // Construct mapping of import paths to their defined names. + // It is only necessary to look at renaming imports. + imports := make(map[string]string) + for _, imp := range f.Imports { + if imp.Name != nil && imp.Name.Name != "_" { + path, _ := strconv.Unquote(imp.Path.Value) + imports[path] = imp.Name.Name + } + } + + // Define qualifier to replace full package paths with names of the imports. + return func(p *types.Package) string { + if p == nil || p == pkg { + return "" + } + + if name, ok := imports[p.Path()]; ok { + if name == "." { + return "" + } else { + return name + } + } + + // If there is no local renaming, fall back to the package name. + return p.Name() + } +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/recv.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/recv.go index ba6f4f4ebd522f..8352ea761736a4 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/recv.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/recv.go @@ -11,6 +11,9 @@ import ( // ReceiverNamed returns the named type (if any) associated with the // type of recv, which may be of the form N or *N, or aliases thereof. // It also reports whether a Pointer was present. +// +// The named result may be nil if recv is from a method on an +// anonymous interface or struct types or in ill-typed code. func ReceiverNamed(recv *types.Var) (isPtr bool, named *types.Named) { t := recv.Type() if ptr, ok := types.Unalias(t).(*types.Pointer); ok { diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/types.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/types.go index df3ea52125439c..34534879630c33 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/types.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/types.go @@ -82,6 +82,7 @@ func NameRelativeTo(pkg *types.Package) types.Qualifier { type NamedOrAlias interface { types.Type Obj() *types.TypeName + // TODO(hxjiang): add method TypeArgs() *types.TypeList after stop supporting go1.22. } // TypeParams is a light shim around t.TypeParams(). @@ -119,3 +120,8 @@ func Origin(t NamedOrAlias) NamedOrAlias { } return t } + +// IsPackageLevel reports whether obj is a package-level symbol. +func IsPackageLevel(obj types.Object) bool { + return obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() +} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/varkind.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/varkind.go new file mode 100644 index 00000000000000..e5da0495111ba1 --- /dev/null +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/varkind.go @@ -0,0 +1,40 @@ +// Copyright 2024 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. + +package typesinternal + +// TODO(adonovan): when CL 645115 lands, define the go1.25 version of +// this API that actually does something. + +import "go/types" + +type VarKind uint8 + +const ( + _ VarKind = iota // (not meaningful) + PackageVar // a package-level variable + LocalVar // a local variable + RecvVar // a method receiver variable + ParamVar // a function parameter variable + ResultVar // a function result variable + FieldVar // a struct field +) + +func (kind VarKind) String() string { + return [...]string{ + 0: "VarKind(0)", + PackageVar: "PackageVar", + LocalVar: "LocalVar", + RecvVar: "RecvVar", + ParamVar: "ParamVar", + ResultVar: "ResultVar", + FieldVar: "FieldVar", + }[kind] +} + +// GetVarKind returns an invalid VarKind. +func GetVarKind(v *types.Var) VarKind { return 0 } + +// SetVarKind has no effect. +func SetVarKind(v *types.Var, kind VarKind) {} diff --git a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go index 1066980649e091..d272949c177189 100644 --- a/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go +++ b/src/cmd/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go @@ -9,62 +9,97 @@ import ( "go/ast" "go/token" "go/types" - "strconv" "strings" ) -// ZeroString returns the string representation of the "zero" value of the type t. +// ZeroString returns the string representation of the zero value for any type t. +// The boolean result indicates whether the type is or contains an invalid type +// or a non-basic (constraint) interface type. +// +// Even for invalid input types, ZeroString may return a partially correct +// string representation. The caller should use the returned isValid boolean +// to determine the validity of the expression. +// +// When assigning to a wider type (such as 'any'), it's the caller's +// responsibility to handle any necessary type conversions. +// // This string can be used on the right-hand side of an assignment where the // left-hand side has that explicit type. +// References to named types are qualified by an appropriate (optional) +// qualifier function. // Exception: This does not apply to tuples. Their string representation is // informational only and cannot be used in an assignment. -// When assigning to a wider type (such as 'any'), it's the caller's -// responsibility to handle any necessary type conversions. +// // See [ZeroExpr] for a variant that returns an [ast.Expr]. -func ZeroString(t types.Type, qf types.Qualifier) string { +func ZeroString(t types.Type, qual types.Qualifier) (_ string, isValid bool) { switch t := t.(type) { case *types.Basic: switch { case t.Info()&types.IsBoolean != 0: - return "false" + return "false", true case t.Info()&types.IsNumeric != 0: - return "0" + return "0", true case t.Info()&types.IsString != 0: - return `""` + return `""`, true case t.Kind() == types.UnsafePointer: fallthrough case t.Kind() == types.UntypedNil: - return "nil" + return "nil", true + case t.Kind() == types.Invalid: + return "invalid", false default: - panic(fmt.Sprint("ZeroString for unexpected type:", t)) + panic(fmt.Sprintf("ZeroString for unexpected type %v", t)) } - case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: - return "nil" + case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature: + return "nil", true + + case *types.Interface: + if !t.IsMethodSet() { + return "invalid", false + } + return "nil", true - case *types.Named, *types.Alias: + case *types.Named: switch under := t.Underlying().(type) { case *types.Struct, *types.Array: - return types.TypeString(t, qf) + "{}" + return types.TypeString(t, qual) + "{}", true + default: + return ZeroString(under, qual) + } + + case *types.Alias: + switch t.Underlying().(type) { + case *types.Struct, *types.Array: + return types.TypeString(t, qual) + "{}", true default: - return ZeroString(under, qf) + // A type parameter can have alias but alias type's underlying type + // can never be a type parameter. + // Use types.Unalias to preserve the info of type parameter instead + // of call Underlying() going right through and get the underlying + // type of the type parameter which is always an interface. + return ZeroString(types.Unalias(t), qual) } case *types.Array, *types.Struct: - return types.TypeString(t, qf) + "{}" + return types.TypeString(t, qual) + "{}", true case *types.TypeParam: // Assumes func new is not shadowed. - return "*new(" + types.TypeString(t, qf) + ")" + return "*new(" + types.TypeString(t, qual) + ")", true case *types.Tuple: // Tuples are not normal values. // We are currently format as "(t[0], ..., t[n])". Could be something else. + isValid := true components := make([]string, t.Len()) for i := 0; i < t.Len(); i++ { - components[i] = ZeroString(t.At(i).Type(), qf) + comp, ok := ZeroString(t.At(i).Type(), qual) + + components[i] = comp + isValid = isValid && ok } - return "(" + strings.Join(components, ", ") + ")" + return "(" + strings.Join(components, ", ") + ")", isValid case *types.Union: // Variables of these types cannot be created, so it makes @@ -76,45 +111,72 @@ func ZeroString(t types.Type, qf types.Qualifier) string { } } -// ZeroExpr returns the ast.Expr representation of the "zero" value of the type t. -// ZeroExpr is defined for types that are suitable for variables. -// It may panic for other types such as Tuple or Union. +// ZeroExpr returns the ast.Expr representation of the zero value for any type t. +// The boolean result indicates whether the type is or contains an invalid type +// or a non-basic (constraint) interface type. +// +// Even for invalid input types, ZeroExpr may return a partially correct ast.Expr +// representation. The caller should use the returned isValid boolean to determine +// the validity of the expression. +// +// This function is designed for types suitable for variables and should not be +// used with Tuple or Union types.References to named types are qualified by an +// appropriate (optional) qualifier function. +// // See [ZeroString] for a variant that returns a string. -func ZeroExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { - switch t := typ.(type) { +func ZeroExpr(t types.Type, qual types.Qualifier) (_ ast.Expr, isValid bool) { + switch t := t.(type) { case *types.Basic: switch { case t.Info()&types.IsBoolean != 0: - return &ast.Ident{Name: "false"} + return &ast.Ident{Name: "false"}, true case t.Info()&types.IsNumeric != 0: - return &ast.BasicLit{Kind: token.INT, Value: "0"} + return &ast.BasicLit{Kind: token.INT, Value: "0"}, true case t.Info()&types.IsString != 0: - return &ast.BasicLit{Kind: token.STRING, Value: `""`} + return &ast.BasicLit{Kind: token.STRING, Value: `""`}, true case t.Kind() == types.UnsafePointer: fallthrough case t.Kind() == types.UntypedNil: - return ast.NewIdent("nil") + return ast.NewIdent("nil"), true + case t.Kind() == types.Invalid: + return &ast.BasicLit{Kind: token.STRING, Value: `"invalid"`}, false default: - panic(fmt.Sprint("ZeroExpr for unexpected type:", t)) + panic(fmt.Sprintf("ZeroExpr for unexpected type %v", t)) } - case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: - return ast.NewIdent("nil") + case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature: + return ast.NewIdent("nil"), true + + case *types.Interface: + if !t.IsMethodSet() { + return &ast.BasicLit{Kind: token.STRING, Value: `"invalid"`}, false + } + return ast.NewIdent("nil"), true - case *types.Named, *types.Alias: + case *types.Named: switch under := t.Underlying().(type) { case *types.Struct, *types.Array: return &ast.CompositeLit{ - Type: TypeExpr(f, pkg, typ), - } + Type: TypeExpr(t, qual), + }, true default: - return ZeroExpr(f, pkg, under) + return ZeroExpr(under, qual) + } + + case *types.Alias: + switch t.Underlying().(type) { + case *types.Struct, *types.Array: + return &ast.CompositeLit{ + Type: TypeExpr(t, qual), + }, true + default: + return ZeroExpr(types.Unalias(t), qual) } case *types.Array, *types.Struct: return &ast.CompositeLit{ - Type: TypeExpr(f, pkg, typ), - } + Type: TypeExpr(t, qual), + }, true case *types.TypeParam: return &ast.StarExpr{ // *new(T) @@ -125,7 +187,7 @@ func ZeroExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { ast.NewIdent(t.Obj().Name()), }, }, - } + }, true case *types.Tuple: // Unlike ZeroString, there is no ast.Expr can express tuple by @@ -157,16 +219,14 @@ func IsZeroExpr(expr ast.Expr) bool { } // TypeExpr returns syntax for the specified type. References to named types -// from packages other than pkg are qualified by an appropriate package name, as -// defined by the import environment of file. +// are qualified by an appropriate (optional) qualifier function. // It may panic for types such as Tuple or Union. -func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { - switch t := typ.(type) { +func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr { + switch t := t.(type) { case *types.Basic: switch t.Kind() { case types.UnsafePointer: - // TODO(hxjiang): replace the implementation with types.Qualifier. - return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")} + return &ast.SelectorExpr{X: ast.NewIdent(qual(types.NewPackage("unsafe", "unsafe"))), Sel: ast.NewIdent("Pointer")} default: return ast.NewIdent(t.Name()) } @@ -174,7 +234,7 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { case *types.Pointer: return &ast.UnaryExpr{ Op: token.MUL, - X: TypeExpr(f, pkg, t.Elem()), + X: TypeExpr(t.Elem(), qual), } case *types.Array: @@ -183,18 +243,18 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { Kind: token.INT, Value: fmt.Sprintf("%d", t.Len()), }, - Elt: TypeExpr(f, pkg, t.Elem()), + Elt: TypeExpr(t.Elem(), qual), } case *types.Slice: return &ast.ArrayType{ - Elt: TypeExpr(f, pkg, t.Elem()), + Elt: TypeExpr(t.Elem(), qual), } case *types.Map: return &ast.MapType{ - Key: TypeExpr(f, pkg, t.Key()), - Value: TypeExpr(f, pkg, t.Elem()), + Key: TypeExpr(t.Key(), qual), + Value: TypeExpr(t.Elem(), qual), } case *types.Chan: @@ -204,14 +264,14 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { } return &ast.ChanType{ Dir: dir, - Value: TypeExpr(f, pkg, t.Elem()), + Value: TypeExpr(t.Elem(), qual), } case *types.Signature: var params []*ast.Field for i := 0; i < t.Params().Len(); i++ { params = append(params, &ast.Field{ - Type: TypeExpr(f, pkg, t.Params().At(i).Type()), + Type: TypeExpr(t.Params().At(i).Type(), qual), Names: []*ast.Ident{ { Name: t.Params().At(i).Name(), @@ -226,7 +286,7 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { var returns []*ast.Field for i := 0; i < t.Results().Len(); i++ { returns = append(returns, &ast.Field{ - Type: TypeExpr(f, pkg, t.Results().At(i).Type()), + Type: TypeExpr(t.Results().At(i).Type(), qual), }) } return &ast.FuncType{ @@ -238,23 +298,9 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { }, } - case interface{ Obj() *types.TypeName }: // *types.{Alias,Named,TypeParam} - switch t.Obj().Pkg() { - case pkg, nil: - return ast.NewIdent(t.Obj().Name()) - } - pkgName := t.Obj().Pkg().Name() - - // TODO(hxjiang): replace the implementation with types.Qualifier. - // If the file already imports the package under another name, use that. - for _, cand := range f.Imports { - if path, _ := strconv.Unquote(cand.Path.Value); path == t.Obj().Pkg().Path() { - if cand.Name != nil && cand.Name.Name != "" { - pkgName = cand.Name.Name - } - } - } - if pkgName == "." { + case *types.TypeParam: + pkgName := qual(t.Obj().Pkg()) + if pkgName == "" || t.Obj().Pkg() == nil { return ast.NewIdent(t.Obj().Name()) } return &ast.SelectorExpr{ @@ -262,6 +308,36 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { Sel: ast.NewIdent(t.Obj().Name()), } + // types.TypeParam also implements interface NamedOrAlias. To differentiate, + // case TypeParam need to be present before case NamedOrAlias. + // TODO(hxjiang): remove this comment once TypeArgs() is added to interface + // NamedOrAlias. + case NamedOrAlias: + var expr ast.Expr = ast.NewIdent(t.Obj().Name()) + if pkgName := qual(t.Obj().Pkg()); pkgName != "." && pkgName != "" { + expr = &ast.SelectorExpr{ + X: ast.NewIdent(pkgName), + Sel: expr.(*ast.Ident), + } + } + + // TODO(hxjiang): call t.TypeArgs after adding method TypeArgs() to + // typesinternal.NamedOrAlias. + if hasTypeArgs, ok := t.(interface{ TypeArgs() *types.TypeList }); ok { + if typeArgs := hasTypeArgs.TypeArgs(); typeArgs != nil && typeArgs.Len() > 0 { + var indices []ast.Expr + for i := range typeArgs.Len() { + indices = append(indices, TypeExpr(typeArgs.At(i), qual)) + } + expr = &ast.IndexListExpr{ + X: expr, + Indices: indices, + } + } + } + + return expr + case *types.Struct: return ast.NewIdent(t.String()) @@ -269,9 +345,43 @@ func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { return ast.NewIdent(t.String()) case *types.Union: - // TODO(hxjiang): handle the union through syntax (~A | ... | ~Z). - // Remove nil check when calling typesinternal.TypeExpr. - return nil + if t.Len() == 0 { + panic("Union type should have at least one term") + } + // Same as go/ast, the return expression will put last term in the + // Y field at topmost level of BinaryExpr. + // For union of type "float32 | float64 | int64", the structure looks + // similar to: + // { + // X: { + // X: float32, + // Op: | + // Y: float64, + // } + // Op: |, + // Y: int64, + // } + var union ast.Expr + for i := range t.Len() { + term := t.Term(i) + termExpr := TypeExpr(term.Type(), qual) + if term.Tilde() { + termExpr = &ast.UnaryExpr{ + Op: token.TILDE, + X: termExpr, + } + } + if i == 0 { + union = termExpr + } else { + union = &ast.BinaryExpr{ + X: union, + Op: token.OR, + Y: termExpr, + } + } + } + return union case *types.Tuple: panic("invalid input type types.Tuple") diff --git a/src/cmd/vendor/modules.txt b/src/cmd/vendor/modules.txt index 281989b1e2c5c2..24a3c19576bc28 100644 --- a/src/cmd/vendor/modules.txt +++ b/src/cmd/vendor/modules.txt @@ -1,4 +1,4 @@ -# github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 +# github.com/google/pprof v0.0.0-20250208200701-d0013a598941 ## explicit; go 1.22 github.com/google/pprof/driver github.com/google/pprof/internal/binutils @@ -16,7 +16,7 @@ github.com/google/pprof/third_party/svgpan # github.com/ianlancetaylor/demangle v0.0.0-20240912202439-0a2b6291aafd ## explicit; go 1.13 github.com/ianlancetaylor/demangle -# golang.org/x/arch v0.12.0 +# golang.org/x/arch v0.14.0 ## explicit; go 1.18 golang.org/x/arch/arm/armasm golang.org/x/arch/arm64/arm64asm @@ -25,10 +25,10 @@ golang.org/x/arch/ppc64/ppc64asm golang.org/x/arch/riscv64/riscv64asm golang.org/x/arch/s390x/s390xasm golang.org/x/arch/x86/x86asm -# golang.org/x/build v0.0.0-20241205234318-b850320af2a4 +# golang.org/x/build v0.0.0-20250211223606-a5e3f75caa63 ## explicit; go 1.22.0 golang.org/x/build/relnote -# golang.org/x/mod v0.22.0 +# golang.org/x/mod v0.23.0 ## explicit; go 1.22.0 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile @@ -39,16 +39,16 @@ golang.org/x/mod/sumdb/dirhash golang.org/x/mod/sumdb/note golang.org/x/mod/sumdb/tlog golang.org/x/mod/zip -# golang.org/x/sync v0.10.0 +# golang.org/x/sync v0.11.0 ## explicit; go 1.18 golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.28.0 +# golang.org/x/sys v0.30.0 ## explicit; go 1.18 golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/telemetry v0.0.0-20241204182053-c0ac0e154df3 +# golang.org/x/telemetry v0.0.0-20250212145848-75305293b65a ## explicit; go 1.22.0 golang.org/x/telemetry golang.org/x/telemetry/counter @@ -60,10 +60,10 @@ golang.org/x/telemetry/internal/crashmonitor golang.org/x/telemetry/internal/mmap golang.org/x/telemetry/internal/telemetry golang.org/x/telemetry/internal/upload -# golang.org/x/term v0.27.0 +# golang.org/x/term v0.29.0 ## explicit; go 1.18 golang.org/x/term -# golang.org/x/text v0.21.0 +# golang.org/x/text v0.22.0 ## explicit; go 1.18 golang.org/x/text/cases golang.org/x/text/internal @@ -73,7 +73,7 @@ golang.org/x/text/internal/tag golang.org/x/text/language golang.org/x/text/transform golang.org/x/text/unicode/norm -# golang.org/x/tools v0.28.0 +# golang.org/x/tools v0.30.1-0.20250212161021-f9aad7054b5f ## explicit; go 1.22.0 golang.org/x/tools/cmd/bisect golang.org/x/tools/cover @@ -122,8 +122,10 @@ golang.org/x/tools/go/types/objectpath golang.org/x/tools/go/types/typeutil golang.org/x/tools/internal/aliases golang.org/x/tools/internal/analysisinternal +golang.org/x/tools/internal/astutil/edge golang.org/x/tools/internal/bisect golang.org/x/tools/internal/facts +golang.org/x/tools/internal/fmtstr golang.org/x/tools/internal/stdlib golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal diff --git a/src/cmd/vet/testdata/print/print.go b/src/cmd/vet/testdata/print/print.go index a2ad0f1298aa79..e00222c42b5aef 100644 --- a/src/cmd/vet/testdata/print/print.go +++ b/src/cmd/vet/testdata/print/print.go @@ -200,8 +200,8 @@ func PrintfTests() { // Bad argument reorderings. Printf("%[xd", 3) // ERROR "Printf format %\[xd is missing closing \]" Printf("%[x]d x", 3) // ERROR "Printf format has invalid argument index \[x\]" - Printf("%[3]*s x", "hi", 2) // ERROR "Printf format has invalid argument index \[3\]" - _ = fmt.Sprintf("%[3]d x", 2) // ERROR "Sprintf format has invalid argument index \[3\]" + Printf("%[3]*s x", "hi", 2) // ERROR "Printf format %\[3\]\*s reads arg #3, but call has 2 args" + _ = fmt.Sprintf("%[3]d x", 2) // ERROR "Sprintf format %\[3\]d reads arg #3, but call has 1 arg" Printf("%[2]*.[1]*[3]d x", 2, "hi", 4) // ERROR "Printf format %\[2]\*\.\[1\]\*\[3\]d uses non-int \x22hi\x22 as argument of \*" Printf("%[0]s x", "arg1") // ERROR "Printf format has invalid argument index \[0\]" Printf("%[0]d x", 1) // ERROR "Printf format has invalid argument index \[0\]" @@ -678,3 +678,12 @@ func PointersToCompoundTypes() { } fmt.Printf("%s\n", T1{&T2{"x"}}) // ERROR "Printf format %s has arg T1{&T2{.x.}} of wrong type .*print\.T1" } + +// Regression test for #68796: materialized aliases cause printf +// checker not to recognize "any" as identical to "interface{}". +func printfUsingAnyNotEmptyInterface(format string, args ...any) { + _ = fmt.Sprintf(format, args...) +} +func _() { + printfUsingAnyNotEmptyInterface("%s", 123) // ERROR "wrong type" +} diff --git a/src/cmd/vet/vet_test.go b/src/cmd/vet/vet_test.go index f1450dcbd28d2c..3860895a0ae96c 100644 --- a/src/cmd/vet/vet_test.go +++ b/src/cmd/vet/vet_test.go @@ -108,7 +108,7 @@ func TestVet(t *testing.T) { // is a no-op for files whose version >= go1.22, so we use a // go.mod file in the rangeloop directory to "downgrade". // - // TOOD(adonovan): delete when go1.21 goes away. + // TODO(adonovan): delete when go1.21 goes away. t.Run("loopclosure", func(t *testing.T) { cmd := testenv.Command(t, testenv.GoToolPath(t), "vet", "-vettool="+vetPath(t), ".") cmd.Env = append(os.Environ(), "GOWORK=off") diff --git a/src/context/context.go b/src/context/context.go index db8bc69553ebe8..bef9e8aab054d6 100644 --- a/src/context/context.go +++ b/src/context/context.go @@ -10,23 +10,25 @@ // calls to servers should accept a Context. The chain of function // calls between them must propagate the Context, optionally replacing // it with a derived Context created using [WithCancel], [WithDeadline], -// [WithTimeout], or [WithValue]. When a Context is canceled, all -// Contexts derived from it are also canceled. +// [WithTimeout], or [WithValue]. +// +// A Context may be canceled to indicate that work done on its behalf should stop. +// A Context with a deadline is canceled after the deadline passes. +// When a Context is canceled, all Contexts derived from it are also canceled. // // The [WithCancel], [WithDeadline], and [WithTimeout] functions take a // Context (the parent) and return a derived Context (the child) and a -// [CancelFunc]. Calling the CancelFunc cancels the child and its +// [CancelFunc]. Calling the CancelFunc directly cancels the child and its // children, removes the parent's reference to the child, and stops // any associated timers. Failing to call the CancelFunc leaks the -// child and its children until the parent is canceled or the timer -// fires. The go vet tool checks that CancelFuncs are used on all -// control-flow paths. +// child and its children until the parent is canceled. The go vet tool +// checks that CancelFuncs are used on all control-flow paths. // -// The [WithCancelCause] function returns a [CancelCauseFunc], which -// takes an error and records it as the cancellation cause. Calling -// [Cause] on the canceled context or any of its children retrieves -// the cause. If no cause is specified, Cause(ctx) returns the same -// value as ctx.Err(). +// The [WithCancelCause], [WithDeadlineCause], and [WithTimeoutCause] functions +// return a [CancelCauseFunc], which takes an error and records it as +// the cancellation cause. Calling [Cause] on the canceled context +// or any of its children retrieves the cause. If no cause is specified, +// Cause(ctx) returns the same value as ctx.Err(). // // Programs that use Contexts should follow these rules to keep interfaces // consistent across packages and enable static analysis tools to check context @@ -107,8 +109,8 @@ type Context interface { // If Done is not yet closed, Err returns nil. // If Done is closed, Err returns a non-nil error explaining why: - // Canceled if the context was canceled - // or DeadlineExceeded if the context's deadline passed. + // DeadlineExceeded if the context's deadline passed, + // or Canceled if the context was canceled for some other reason. // After Err returns a non-nil error, successive calls to Err return the same error. Err() error @@ -160,11 +162,12 @@ type Context interface { Value(key any) any } -// Canceled is the error returned by [Context.Err] when the context is canceled. +// Canceled is the error returned by [Context.Err] when the context is canceled +// for some reason other than its deadline passing. var Canceled = errors.New("context canceled") -// DeadlineExceeded is the error returned by [Context.Err] when the context's -// deadline passes. +// DeadlineExceeded is the error returned by [Context.Err] when the context is canceled +// due to its deadline passing. var DeadlineExceeded error = deadlineExceededError{} type deadlineExceededError struct{} @@ -296,9 +299,8 @@ func Cause(c Context) error { return c.Err() } -// AfterFunc arranges to call f in its own goroutine after ctx is done -// (canceled or timed out). -// If ctx is already done, AfterFunc calls f immediately in its own goroutine. +// AfterFunc arranges to call f in its own goroutine after ctx is canceled. +// If ctx is already canceled, AfterFunc calls f immediately in its own goroutine. // // Multiple calls to AfterFunc on a context operate independently; // one does not replace another. @@ -306,7 +308,7 @@ func Cause(c Context) error { // Calling the returned stop function stops the association of ctx with f. // It returns true if the call stopped f from being run. // If stop returns false, -// either the context is done and f has been started in its own goroutine; +// either the context is canceled and f has been started in its own goroutine; // or f was already stopped. // The stop function does not wait for f to complete before returning. // If the caller needs to know whether f is completed, diff --git a/src/context/example_test.go b/src/context/example_test.go index b597b09f16e525..be8cd8376e1736 100644 --- a/src/context/example_test.go +++ b/src/context/example_test.go @@ -146,8 +146,8 @@ func ExampleAfterFunc_cond() { defer stopf() // Since the wakeups are using Broadcast instead of Signal, this call to - // Wait may unblock due to some other goroutine's context becoming done, - // so to be sure that ctx is actually done we need to check it in a loop. + // Wait may unblock due to some other goroutine's context being canceled, + // so to be sure that ctx is actually canceled we need to check it in a loop. for !conditionMet() { cond.Wait() if ctx.Err() != nil { diff --git a/src/crypto/aes/aes.go b/src/crypto/aes/aes.go index 5bc2d13d673e0a..22ea8819ed239a 100644 --- a/src/crypto/aes/aes.go +++ b/src/crypto/aes/aes.go @@ -30,7 +30,7 @@ func (k KeySizeError) Error() string { } // NewCipher creates and returns a new [cipher.Block]. -// The key argument should be the AES key, +// The key argument must be the AES key, // either 16, 24, or 32 bytes to select // AES-128, AES-192, or AES-256. func NewCipher(key []byte) (cipher.Block, error) { diff --git a/src/crypto/cipher/cbc.go b/src/crypto/cipher/cbc.go index b4536aceb9c224..8e614062969954 100644 --- a/src/crypto/cipher/cbc.go +++ b/src/crypto/cipher/cbc.go @@ -15,6 +15,7 @@ import ( "bytes" "crypto/internal/fips140/aes" "crypto/internal/fips140/alias" + "crypto/internal/fips140only" "crypto/subtle" ) @@ -53,6 +54,9 @@ func NewCBCEncrypter(b Block, iv []byte) BlockMode { if b, ok := b.(*aes.Block); ok { return aes.NewCBCEncrypter(b, [16]byte(iv)) } + if fips140only.Enabled { + panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") + } if cbc, ok := b.(cbcEncAble); ok { return cbc.NewCBCEncrypter(iv) } @@ -129,6 +133,9 @@ func NewCBCDecrypter(b Block, iv []byte) BlockMode { if b, ok := b.(*aes.Block); ok { return aes.NewCBCDecrypter(b, [16]byte(iv)) } + if fips140only.Enabled { + panic("crypto/cipher: use of CBC with non-AES ciphers is not allowed in FIPS 140-only mode") + } if cbc, ok := b.(cbcDecAble); ok { return cbc.NewCBCDecrypter(iv) } diff --git a/src/crypto/cipher/ctr.go b/src/crypto/cipher/ctr.go index c868635b8a7a86..49512ca5dd8b8e 100644 --- a/src/crypto/cipher/ctr.go +++ b/src/crypto/cipher/ctr.go @@ -16,6 +16,7 @@ import ( "bytes" "crypto/internal/fips140/aes" "crypto/internal/fips140/alias" + "crypto/internal/fips140only" "crypto/subtle" ) @@ -41,6 +42,9 @@ func NewCTR(block Block, iv []byte) Stream { if block, ok := block.(*aes.Block); ok { return aesCtrWrapper{aes.NewCTR(block, iv)} } + if fips140only.Enabled { + panic("crypto/cipher: use of CTR with non-AES ciphers is not allowed in FIPS 140-only mode") + } if ctr, ok := block.(ctrAble); ok { return ctr.NewCTR(iv) } diff --git a/src/crypto/ecdh/nist.go b/src/crypto/ecdh/nist.go index 0f4a65e5affb80..acef8298943c2b 100644 --- a/src/crypto/ecdh/nist.go +++ b/src/crypto/ecdh/nist.go @@ -8,6 +8,7 @@ import ( "bytes" "crypto/internal/boring" "crypto/internal/fips140/ecdh" + "crypto/internal/fips140only" "errors" "io" ) @@ -43,6 +44,10 @@ func (c *nistCurve) GenerateKey(rand io.Reader) (*PrivateKey, error) { return k, nil } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/ecdh: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } + privateKey, err := c.generate(rand) if err != nil { return nil, err diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index 0ad669795c56b2..cb308b41e9df86 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package ecdsa implements the Elliptic Curve Digital Signature Algorithm, as -// defined in FIPS 186-5 and SEC 1, Version 2.0. +// defined in [FIPS 186-5]. // // Signatures generated by this package are not deterministic, but entropy is // mixed with the private key and the message, achieving the same level of @@ -12,6 +12,8 @@ // Operations involving private keys are implemented using constant-time // algorithms, as long as an [elliptic.Curve] returned by [elliptic.P224], // [elliptic.P256], [elliptic.P384], or [elliptic.P521] is used. +// +// [FIPS 186-5]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf package ecdsa import ( @@ -21,6 +23,8 @@ import ( "crypto/internal/boring" "crypto/internal/boring/bbig" "crypto/internal/fips140/ecdsa" + "crypto/internal/fips140hash" + "crypto/internal/fips140only" "crypto/internal/randutil" "crypto/sha512" "crypto/subtle" @@ -182,6 +186,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) { } func generateFIPS[P ecdsa.Point[P]](curve elliptic.Curve, c *ecdsa.Curve[P], rand io.Reader) (*PrivateKey, error) { + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } privateKey, err := ecdsa.GenerateKey(c, rand) if err != nil { return nil, err @@ -228,6 +235,9 @@ func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) { } func signFIPS[P ecdsa.Point[P]](c *ecdsa.Curve[P], priv *PrivateKey, rand io.Reader, hash []byte) ([]byte, error) { + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } // privateKeyToFIPS is very slow in FIPS mode because it performs a // Sign+Verify cycle per FIPS 140-3 IG 10.3.A. We should find a way to cache // it or attach it to the PrivateKey. @@ -272,7 +282,11 @@ func signFIPSDeterministic[P ecdsa.Point[P]](c *ecdsa.Curve[P], hashFunc crypto. if err != nil { return nil, err } - sig, err := ecdsa.SignDeterministic(c, hashFunc.New, k, hash) + h := fips140hash.UnwrapNew(hashFunc.New) + if fips140only.Enabled && !fips140only.ApprovedHash(h()) { + return nil, errors.New("crypto/ecdsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + sig, err := ecdsa.SignDeterministic(c, h, k, hash) if err != nil { return nil, err } diff --git a/src/crypto/fips140/fips140.go b/src/crypto/fips140/fips140.go index 9fd8fe76e5efd3..1c4036d5e74735 100644 --- a/src/crypto/fips140/fips140.go +++ b/src/crypto/fips140/fips140.go @@ -10,7 +10,7 @@ import ( "internal/godebug" ) -var fips140GODEBUG = godebug.New("#fips140") +var fips140GODEBUG = godebug.New("fips140") // Enabled reports whether the cryptography libraries are operating in FIPS // 140-3 mode. @@ -26,7 +26,7 @@ func Enabled() bool { if currentlyEnabled != fips140.Enabled { panic("crypto/fips140: GODEBUG setting changed after program start") } - if fips140.Enabled && !check.Enabled() { + if fips140.Enabled && !check.Verified { panic("crypto/fips140: FIPS 140-3 mode enabled, but integrity check didn't pass") } return fips140.Enabled diff --git a/src/crypto/hkdf/hkdf.go b/src/crypto/hkdf/hkdf.go index 7cfbe2c60de356..6b02522866d57f 100644 --- a/src/crypto/hkdf/hkdf.go +++ b/src/crypto/hkdf/hkdf.go @@ -12,6 +12,7 @@ package hkdf import ( "crypto/internal/fips140/hkdf" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "errors" "hash" @@ -24,10 +25,11 @@ import ( // Expand invocations and different context values. Most common scenarios, // including the generation of multiple keys, should use [Key] instead. func Extract[H hash.Hash](h func() H, secret, salt []byte) ([]byte, error) { - if err := checkFIPS140Only(h, secret); err != nil { + fh := fips140hash.UnwrapNew(h) + if err := checkFIPS140Only(fh, secret); err != nil { return nil, err } - return hkdf.Extract(h, secret, salt), nil + return hkdf.Extract(fh, secret, salt), nil } // Expand derives a key from the given hash, key, and optional context info, @@ -38,35 +40,37 @@ func Extract[H hash.Hash](h func() H, secret, salt []byte) ([]byte, error) { // random or pseudorandom cryptographically strong key. See RFC 5869, Section // 3.3. Most common scenarios will want to use [Key] instead. func Expand[H hash.Hash](h func() H, pseudorandomKey []byte, info string, keyLength int) ([]byte, error) { - if err := checkFIPS140Only(h, pseudorandomKey); err != nil { + fh := fips140hash.UnwrapNew(h) + if err := checkFIPS140Only(fh, pseudorandomKey); err != nil { return nil, err } - limit := h().Size() * 255 + limit := fh().Size() * 255 if keyLength > limit { return nil, errors.New("hkdf: requested key length too large") } - return hkdf.Expand(h, pseudorandomKey, info, keyLength), nil + return hkdf.Expand(fh, pseudorandomKey, info, keyLength), nil } // Key derives a key from the given hash, secret, salt and context info, // returning a []byte of length keyLength that can be used as cryptographic key. // Salt and info can be nil. func Key[Hash hash.Hash](h func() Hash, secret, salt []byte, info string, keyLength int) ([]byte, error) { - if err := checkFIPS140Only(h, secret); err != nil { + fh := fips140hash.UnwrapNew(h) + if err := checkFIPS140Only(fh, secret); err != nil { return nil, err } - limit := h().Size() * 255 + limit := fh().Size() * 255 if keyLength > limit { return nil, errors.New("hkdf: requested key length too large") } - return hkdf.Key(h, secret, salt, info, keyLength), nil + return hkdf.Key(fh, secret, salt, info, keyLength), nil } -func checkFIPS140Only[H hash.Hash](h func() H, key []byte) error { +func checkFIPS140Only[Hash hash.Hash](h func() Hash, key []byte) error { if !fips140only.Enabled { return nil } diff --git a/src/crypto/hkdf/hkdf_test.go b/src/crypto/hkdf/hkdf_test.go index 201b440289bb2d..57d90f88e93e75 100644 --- a/src/crypto/hkdf/hkdf_test.go +++ b/src/crypto/hkdf/hkdf_test.go @@ -404,6 +404,9 @@ func TestFIPSServiceIndicator(t *testing.T) { // Salt and info are short, which is ok, but translates to a short HMAC key. fips140.ResetServiceIndicator() _, err = Key(sha256.New, []byte("YELLOW SUBMARINE"), []byte("salt"), "info", 32) + if err != nil { + panic(err) + } if !fips140.ServiceIndicator() { t.Error("FIPS service indicator should be set") } diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go index 72f5a4abea9d35..554c8c9b78940b 100644 --- a/src/crypto/hmac/hmac.go +++ b/src/crypto/hmac/hmac.go @@ -24,6 +24,7 @@ package hmac import ( "crypto/internal/boring" "crypto/internal/fips140/hmac" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "crypto/subtle" "hash" @@ -43,6 +44,7 @@ func New(h func() hash.Hash, key []byte) hash.Hash { } // BoringCrypto did not recognize h, so fall through to standard Go code. } + h = fips140hash.UnwrapNew(h) if fips140only.Enabled { if len(key) < 112/8 { panic("crypto/hmac: use of keys shorter than 112 bits is not allowed in FIPS 140-only mode") diff --git a/src/crypto/internal/boring/boring.go b/src/crypto/internal/boring/boring.go index 90cf1edb75bbad..6dfc6ed5f50207 100644 --- a/src/crypto/internal/boring/boring.go +++ b/src/crypto/internal/boring/boring.go @@ -16,6 +16,7 @@ import "C" import ( "crypto/internal/boring/sig" _ "crypto/internal/boring/syso" + "crypto/internal/fips140" "internal/stringslite" "math/bits" "unsafe" @@ -31,6 +32,12 @@ func init() { sig.BoringCrypto() } +func init() { + if fips140.Enabled { + panic("boringcrypto: cannot use GODEBUG=fips140 with GOEXPERIMENT=boringcrypto") + } +} + // Unreachable marks code that should be unreachable // when BoringCrypto is in use. It panics. func Unreachable() { diff --git a/src/crypto/internal/boring/ecdh.go b/src/crypto/internal/boring/ecdh.go index b90e533e7cc38c..4a887fdfd67664 100644 --- a/src/crypto/internal/boring/ecdh.go +++ b/src/crypto/internal/boring/ecdh.go @@ -17,7 +17,6 @@ import ( type PublicKeyECDH struct { curve string key *C.GO_EC_POINT - group *C.GO_EC_GROUP bytes []byte } @@ -59,7 +58,7 @@ func NewPublicKeyECDH(curve string, bytes []byte) (*PublicKeyECDH, error) { return nil, errors.New("point not on curve") } - k := &PublicKeyECDH{curve, key, group, append([]byte(nil), bytes...)} + k := &PublicKeyECDH{curve, key, append([]byte(nil), bytes...)} // Note: Because of the finalizer, any time k.key is passed to cgo, // that call must be followed by a call to runtime.KeepAlive(k), // to make sure k is not collected (and finalized) before the cgo @@ -122,7 +121,7 @@ func (k *PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) { C._goboringcrypto_EC_POINT_free(pt) return nil, err } - pub := &PublicKeyECDH{k.curve, pt, group, bytes} + pub := &PublicKeyECDH{k.curve, pt, bytes} // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive. runtime.SetFinalizer(pub, (*PublicKeyECDH).finalize) return pub, nil @@ -138,6 +137,15 @@ func pointBytesECDH(curve string, group *C.GO_EC_GROUP, pt *C.GO_EC_POINT) ([]by } func ECDH(priv *PrivateKeyECDH, pub *PublicKeyECDH) ([]byte, error) { + // Make sure priv and pub are not garbage collected while we are in a cgo + // call. + // + // The call to xCoordBytesECDH should prevent priv from being collected, but + // include this in case the code is reordered and there is a subsequent call + // cgo call after that point. + defer runtime.KeepAlive(priv) + defer runtime.KeepAlive(pub) + group := C._goboringcrypto_EC_KEY_get0_group(priv.key) if group == nil { return nil, fail("EC_KEY_get0_group") diff --git a/src/crypto/internal/cryptotest/allocations.go b/src/crypto/internal/cryptotest/allocations.go index 0194c2f89dc77e..70055af70b42ec 100644 --- a/src/crypto/internal/cryptotest/allocations.go +++ b/src/crypto/internal/cryptotest/allocations.go @@ -32,6 +32,12 @@ func SkipTestAllocations(t *testing.T) { t.Skip("skipping allocations test on plan9") } + // s390x deviates from other assembly implementations and is very hard to + // test due to the lack of LUCI builders. See #67307. + if runtime.GOARCH == "s390x" { + t.Skip("skipping allocations test on s390x") + } + // Some APIs rely on inliner and devirtualization to allocate on the stack. testenv.SkipIfOptimizationOff(t) } diff --git a/src/crypto/internal/cryptotest/fetchmodule.go b/src/crypto/internal/cryptotest/fetchmodule.go index 740b17b00156e0..37f2a094972b9d 100644 --- a/src/crypto/internal/cryptotest/fetchmodule.go +++ b/src/crypto/internal/cryptotest/fetchmodule.go @@ -9,6 +9,7 @@ import ( "encoding/json" "internal/testenv" "os" + "os/exec" "testing" ) @@ -23,7 +24,11 @@ func FetchModule(t *testing.T, module, version string) string { // instead. (For example, run.bash sets GOPATH=/nonexist-gopath.) out, err := testenv.Command(t, goTool, "env", "GOMODCACHE").Output() if err != nil { - t.Fatalf("%s env GOMODCACHE: %v\n%s", goTool, err, out) + t.Errorf("%s env GOMODCACHE: %v\n%s", goTool, err, out) + if ee, ok := err.(*exec.ExitError); ok { + t.Logf("%s", ee.Stderr) + } + t.FailNow() } modcacheOk := false if gomodcache := string(bytes.TrimSpace(out)); gomodcache != "" { diff --git a/src/crypto/internal/fips140/aes/aes.go b/src/crypto/internal/fips140/aes/aes.go index 739f1a3dbe6599..62f6919eda8178 100644 --- a/src/crypto/internal/fips140/aes/aes.go +++ b/src/crypto/internal/fips140/aes/aes.go @@ -94,6 +94,8 @@ func newBlockExpanded(c *blockExpanded, key []byte) { func (c *Block) BlockSize() int { return BlockSize } func (c *Block) Encrypt(dst, src []byte) { + // AES-ECB is not approved in FIPS 140-3 mode. + fips140.RecordNonApproved() if len(src) < BlockSize { panic("crypto/aes: input not full block") } @@ -103,11 +105,12 @@ func (c *Block) Encrypt(dst, src []byte) { if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { panic("crypto/aes: invalid buffer overlap") } - fips140.RecordApproved() encryptBlock(c, dst, src) } func (c *Block) Decrypt(dst, src []byte) { + // AES-ECB is not approved in FIPS 140-3 mode. + fips140.RecordNonApproved() if len(src) < BlockSize { panic("crypto/aes: input not full block") } @@ -117,6 +120,12 @@ func (c *Block) Decrypt(dst, src []byte) { if alias.InexactOverlap(dst[:BlockSize], src[:BlockSize]) { panic("crypto/aes: invalid buffer overlap") } - fips140.RecordApproved() decryptBlock(c, dst, src) } + +// EncryptBlockInternal applies the AES encryption function to one block. +// +// It is an internal function meant only for the gcm package. +func EncryptBlockInternal(c *Block, dst, src []byte) { + encryptBlock(c, dst, src) +} diff --git a/src/crypto/internal/fips140/aes/cbc.go b/src/crypto/internal/fips140/aes/cbc.go index c7837b9d87d102..a5a079453f7f4c 100644 --- a/src/crypto/internal/fips140/aes/cbc.go +++ b/src/crypto/internal/fips140/aes/cbc.go @@ -5,6 +5,7 @@ package aes import ( + "crypto/internal/fips140" "crypto/internal/fips140/alias" "crypto/internal/fips140/subtle" ) @@ -32,6 +33,7 @@ func (c *CBCEncrypter) CryptBlocks(dst, src []byte) { if alias.InexactOverlap(dst[:len(src)], src) { panic("crypto/cipher: invalid buffer overlap") } + fips140.RecordApproved() if len(src) == 0 { return } @@ -50,7 +52,7 @@ func cryptBlocksEncGeneric(b *Block, civ *[BlockSize]byte, dst, src []byte) { for len(src) > 0 { // Write the xor to dst, then encrypt in place. subtle.XORBytes(dst[:BlockSize], src[:BlockSize], iv) - b.Encrypt(dst[:BlockSize], dst[:BlockSize]) + encryptBlock(b, dst[:BlockSize], dst[:BlockSize]) // Move to the next block with this block as the next iv. iv = dst[:BlockSize] @@ -85,6 +87,7 @@ func (c *CBCDecrypter) CryptBlocks(dst, src []byte) { if alias.InexactOverlap(dst[:len(src)], src) { panic("crypto/cipher: invalid buffer overlap") } + fips140.RecordApproved() if len(src) == 0 { return } @@ -111,7 +114,7 @@ func cryptBlocksDecGeneric(b *Block, civ *[BlockSize]byte, dst, src []byte) { copy(civ[:], src[start:end]) for start >= 0 { - b.Decrypt(dst[start:end], src[start:end]) + decryptBlock(b, dst[start:end], src[start:end]) if start > 0 { subtle.XORBytes(dst[start:end], dst[start:end], src[prev:start]) diff --git a/src/crypto/internal/fips140/aes/ctr.go b/src/crypto/internal/fips140/aes/ctr.go index f612034d85ff1a..2e55d233d3a767 100644 --- a/src/crypto/internal/fips140/aes/ctr.go +++ b/src/crypto/internal/fips140/aes/ctr.go @@ -5,6 +5,7 @@ package aes import ( + "crypto/internal/fips140" "crypto/internal/fips140/alias" "crypto/internal/fips140/subtle" "crypto/internal/fips140deps/byteorder" @@ -71,6 +72,7 @@ func (c *CTR) XORKeyStreamAt(dst, src []byte, offset uint64) { if alias.InexactOverlap(dst, src) { panic("crypto/aes: invalid buffer overlap") } + fips140.RecordApproved() ivlo, ivhi := add128(c.ivlo, c.ivhi, offset/BlockSize) @@ -132,7 +134,7 @@ func ctrBlocks(b *Block, dst, src []byte, ivlo, ivhi uint64) { byteorder.BEPutUint64(buf[i:], ivhi) byteorder.BEPutUint64(buf[i+8:], ivlo) ivlo, ivhi = add128(ivlo, ivhi, 1) - b.Encrypt(buf[i:], buf[i:]) + encryptBlock(b, buf[i:], buf[i:]) } // XOR into buf first, in case src and dst overlap (see above). subtle.XORBytes(buf, src, buf) diff --git a/src/crypto/internal/fips140/aes/gcm/cmac.go b/src/crypto/internal/fips140/aes/gcm/cmac.go index e0a9dc43dede7e..3a979a5c70870f 100644 --- a/src/crypto/internal/fips140/aes/gcm/cmac.go +++ b/src/crypto/internal/fips140/aes/gcm/cmac.go @@ -28,7 +28,7 @@ func NewCMAC(b *aes.Block) *CMAC { } func (c *CMAC) deriveSubkeys() { - c.b.Encrypt(c.k1[:], c.k1[:]) + aes.EncryptBlockInternal(&c.b, c.k1[:], c.k1[:]) msb := shiftLeft(&c.k1) c.k1[len(c.k1)-1] ^= msb * 0b10000111 @@ -45,7 +45,7 @@ func (c *CMAC) MAC(m []byte) [aes.BlockSize]byte { // Special-cased as a single empty partial final block. x = c.k2 x[len(m)] ^= 0b10000000 - c.b.Encrypt(x[:], x[:]) + aes.EncryptBlockInternal(&c.b, x[:], x[:]) return x } for len(m) >= aes.BlockSize { @@ -54,7 +54,7 @@ func (c *CMAC) MAC(m []byte) [aes.BlockSize]byte { // Final complete block. subtle.XORBytes(x[:], c.k1[:], x[:]) } - c.b.Encrypt(x[:], x[:]) + aes.EncryptBlockInternal(&c.b, x[:], x[:]) m = m[aes.BlockSize:] } if len(m) > 0 { @@ -62,7 +62,7 @@ func (c *CMAC) MAC(m []byte) [aes.BlockSize]byte { subtle.XORBytes(x[:], m, x[:]) subtle.XORBytes(x[:], c.k2[:], x[:]) x[len(m)] ^= 0b10000000 - c.b.Encrypt(x[:], x[:]) + aes.EncryptBlockInternal(&c.b, x[:], x[:]) } return x } diff --git a/src/crypto/internal/fips140/aes/gcm/gcm_asm.go b/src/crypto/internal/fips140/aes/gcm/gcm_asm.go index d513f77a2f342b..7924e457dee829 100644 --- a/src/crypto/internal/fips140/aes/gcm/gcm_asm.go +++ b/src/crypto/internal/fips140/aes/gcm/gcm_asm.go @@ -81,7 +81,7 @@ func seal(out []byte, g *GCM, nonce, plaintext, data []byte) { gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) } - g.cipher.Encrypt(tagMask[:], counter[:]) + aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) var tagOut [gcmTagSize]byte gcmAesData(&g.productTable, data, &tagOut) @@ -114,7 +114,7 @@ func open(out []byte, g *GCM, nonce, ciphertext, data []byte) error { gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) } - g.cipher.Encrypt(tagMask[:], counter[:]) + aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) var expectedTag [gcmTagSize]byte gcmAesData(&g.productTable, data, &expectedTag) diff --git a/src/crypto/internal/fips140/aes/gcm/gcm_generic.go b/src/crypto/internal/fips140/aes/gcm/gcm_generic.go index 778392661dae6e..385955ed77838b 100644 --- a/src/crypto/internal/fips140/aes/gcm/gcm_generic.go +++ b/src/crypto/internal/fips140/aes/gcm/gcm_generic.go @@ -12,7 +12,7 @@ import ( func sealGeneric(out []byte, g *GCM, nonce, plaintext, additionalData []byte) { var H, counter, tagMask [gcmBlockSize]byte - g.cipher.Encrypt(H[:], H[:]) + aes.EncryptBlockInternal(&g.cipher, H[:], H[:]) deriveCounterGeneric(&H, &counter, nonce) gcmCounterCryptGeneric(&g.cipher, tagMask[:], tagMask[:], &counter) @@ -25,7 +25,7 @@ func sealGeneric(out []byte, g *GCM, nonce, plaintext, additionalData []byte) { func openGeneric(out []byte, g *GCM, nonce, ciphertext, additionalData []byte) error { var H, counter, tagMask [gcmBlockSize]byte - g.cipher.Encrypt(H[:], H[:]) + aes.EncryptBlockInternal(&g.cipher, H[:], H[:]) deriveCounterGeneric(&H, &counter, nonce) gcmCounterCryptGeneric(&g.cipher, tagMask[:], tagMask[:], &counter) @@ -70,7 +70,7 @@ func gcmCounterCryptGeneric(b *aes.Block, out, src []byte, counter *[gcmBlockSiz var mask [gcmBlockSize]byte for len(src) >= gcmBlockSize { - b.Encrypt(mask[:], counter[:]) + aes.EncryptBlockInternal(b, mask[:], counter[:]) gcmInc32(counter) subtle.XORBytes(out, src, mask[:]) @@ -79,7 +79,7 @@ func gcmCounterCryptGeneric(b *aes.Block, out, src []byte, counter *[gcmBlockSiz } if len(src) > 0 { - b.Encrypt(mask[:], counter[:]) + aes.EncryptBlockInternal(b, mask[:], counter[:]) gcmInc32(counter) subtle.XORBytes(out, src, mask[:]) } diff --git a/src/crypto/internal/fips140/aes/gcm/gcm_ppc64x.go b/src/crypto/internal/fips140/aes/gcm/gcm_ppc64x.go index 5084835e88d315..8d44c75745d9b8 100644 --- a/src/crypto/internal/fips140/aes/gcm/gcm_ppc64x.go +++ b/src/crypto/internal/fips140/aes/gcm/gcm_ppc64x.go @@ -51,7 +51,7 @@ func initGCM(g *GCM) { } hle := make([]byte, gcmBlockSize) - g.cipher.Encrypt(hle, hle) + aes.EncryptBlockInternal(&g.cipher, hle, hle) // Reverse the bytes in each 8 byte chunk // Load little endian, store big endian @@ -133,7 +133,7 @@ func seal(out []byte, g *GCM, nonce, plaintext, data []byte) { var counter, tagMask [gcmBlockSize]byte deriveCounter(&counter, nonce, &g.productTable) - g.cipher.Encrypt(tagMask[:], counter[:]) + aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) gcmInc32(&counter) counterCrypt(&g.cipher, out, plaintext, &counter) @@ -151,7 +151,7 @@ func open(out []byte, g *GCM, nonce, ciphertext, data []byte) error { var counter, tagMask [gcmBlockSize]byte deriveCounter(&counter, nonce, &g.productTable) - g.cipher.Encrypt(tagMask[:], counter[:]) + aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) gcmInc32(&counter) var expectedTag [gcmTagSize]byte diff --git a/src/crypto/internal/fips140/aes/gcm/gcm_s390x.go b/src/crypto/internal/fips140/aes/gcm/gcm_s390x.go index 6d88e1824083be..526f3f9d4a2019 100644 --- a/src/crypto/internal/fips140/aes/gcm/gcm_s390x.go +++ b/src/crypto/internal/fips140/aes/gcm/gcm_s390x.go @@ -55,7 +55,7 @@ func initGCM(g *GCM) { return } // Note that hashKey is also used in the KMA codepath to hash large nonces. - g.cipher.Encrypt(g.hashKey[:], g.hashKey[:]) + aes.EncryptBlockInternal(&g.cipher, g.hashKey[:], g.hashKey[:]) } // ghashAsm uses the GHASH algorithm to hash data with the given key. The initial @@ -115,7 +115,7 @@ func counterCrypt(g *GCM, dst, src []byte, cnt *[gcmBlockSize]byte) { } if len(src) > 0 { var x [16]byte - g.cipher.Encrypt(x[:], cnt[:]) + aes.EncryptBlockInternal(&g.cipher, x[:], cnt[:]) for i := range src { dst[i] = src[i] ^ x[i] } diff --git a/src/crypto/internal/fips140/check/asan.go b/src/crypto/internal/fips140/asan.go similarity index 92% rename from src/crypto/internal/fips140/check/asan.go rename to src/crypto/internal/fips140/asan.go index 2c783483548344..af8f24df8117d2 100644 --- a/src/crypto/internal/fips140/check/asan.go +++ b/src/crypto/internal/fips140/asan.go @@ -4,6 +4,6 @@ //go:build asan -package check +package fips140 const asanEnabled = true diff --git a/src/crypto/internal/fips140/bigmod/nat.go b/src/crypto/internal/fips140/bigmod/nat.go index 2f17f896b3a52f..6757cccd026b11 100644 --- a/src/crypto/internal/fips140/bigmod/nat.go +++ b/src/crypto/internal/fips140/bigmod/nat.go @@ -134,6 +134,13 @@ func (x *Nat) set(y *Nat) *Nat { return x } +// Bits returns x as a little-endian slice of uint. The length of the slice +// matches the announced length of x. The result and x share the same underlying +// array. +func (x *Nat) Bits() []uint { + return x.limbs +} + // Bytes returns x as a zero-extended big-endian byte slice. The size of the // slice will match the size of m. // @@ -1058,6 +1065,34 @@ func (out *Nat) ExpShortVarTime(x *Nat, e uint, m *Modulus) *Nat { // //go:norace func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) { + u, A, err := extendedGCD(a, m.nat) + if err != nil { + return x, false + } + if u.IsOne() == no { + return x, false + } + return x.set(A), true +} + +// GCDVarTime calculates x = GCD(a, b) where at least one of a or b is odd, and +// both are non-zero. If GCDVarTime returns an error, x is not modified. +// +// The output will be resized to the size of the larger of a and b. +func (x *Nat) GCDVarTime(a, b *Nat) (*Nat, error) { + u, _, err := extendedGCD(a, b) + if err != nil { + return nil, err + } + return x.set(u), nil +} + +// extendedGCD computes u and A such that a = GCD(a, m) and u = A*a - B*m. +// +// u will have the size of the larger of a and m, and A will have the size of m. +// +// It is an error if either a or m is zero, or if they are both even. +func extendedGCD(a, m *Nat) (u, A *Nat, err error) { // This is the extended binary GCD algorithm described in the Handbook of // Applied Cryptography, Algorithm 14.61, adapted by BoringSSL to bound // coefficients and avoid negative numbers. For more details and proof of @@ -1068,7 +1103,7 @@ func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) { // 1. Negate [B] and [C] so they are positive. The invariant now involves a // subtraction. // 2. If step 2 (both [x] and [y] are even) runs, abort immediately. This - // algorithm only cares about [x] and [y] relatively prime. + // case needs to be handled by the caller. // 3. Subtract copies of [x] and [y] as needed in step 6 (both [u] and [v] // are odd) so coefficients stay in bounds. // 4. Replace the [u >= v] check with [u > v]. This changes the end @@ -1082,21 +1117,21 @@ func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) { // // Note this algorithm does not handle either input being zero. - if a.IsZero() == yes { - return x, false + if a.IsZero() == yes || m.IsZero() == yes { + return nil, nil, errors.New("extendedGCD: a or m is zero") } - if a.IsOdd() == no && !m.odd { - // a and m are not coprime, as they are both even. - return x, false + if a.IsOdd() == no && m.IsOdd() == no { + return nil, nil, errors.New("extendedGCD: both a and m are even") } - u := NewNat().set(a).ExpandFor(m) - v := m.Nat() + size := max(len(a.limbs), len(m.limbs)) + u = NewNat().set(a).expand(size) + v := NewNat().set(m).expand(size) - A := NewNat().reset(len(m.nat.limbs)) + A = NewNat().reset(len(m.limbs)) A.limbs[0] = 1 B := NewNat().reset(len(a.limbs)) - C := NewNat().reset(len(m.nat.limbs)) + C := NewNat().reset(len(m.limbs)) D := NewNat().reset(len(a.limbs)) D.limbs[0] = 1 @@ -1119,11 +1154,11 @@ func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) { if u.IsOdd() == yes && v.IsOdd() == yes { if v.cmpGeq(u) == no { u.sub(v) - A.Add(C, m) + A.Add(C, &Modulus{nat: m}) B.Add(D, &Modulus{nat: a}) } else { v.sub(u) - C.Add(A, m) + C.Add(A, &Modulus{nat: m}) D.Add(B, &Modulus{nat: a}) } } @@ -1137,7 +1172,7 @@ func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) { if u.IsOdd() == no { rshift1(u, 0) if A.IsOdd() == yes || B.IsOdd() == yes { - rshift1(A, A.add(m.nat)) + rshift1(A, A.add(m)) rshift1(B, B.add(a)) } else { rshift1(A, 0) @@ -1146,7 +1181,7 @@ func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) { } else { // v.IsOdd() == no rshift1(v, 0) if C.IsOdd() == yes || D.IsOdd() == yes { - rshift1(C, C.add(m.nat)) + rshift1(C, C.add(m)) rshift1(D, D.add(a)) } else { rshift1(C, 0) @@ -1155,10 +1190,7 @@ func (x *Nat) InverseVarTime(a *Nat, m *Modulus) (*Nat, bool) { } if v.IsZero() == yes { - if u.IsOne() == no { - return x, false - } - return x.set(A), true + return u, A, nil } } } @@ -1177,3 +1209,20 @@ func rshift1(a *Nat, carry uint) { } } } + +// DivShortVarTime calculates x = x / y and returns the remainder. +// +// It panics if y is zero. +// +//go:norace +func (x *Nat) DivShortVarTime(y uint) uint { + if y == 0 { + panic("bigmod: division by zero") + } + + var r uint + for i := len(x.limbs) - 1; i >= 0; i-- { + x.limbs[i], r = bits.Div(r, x.limbs[i], y) + } + return r +} diff --git a/src/crypto/internal/fips140/boring.go b/src/crypto/internal/fips140/boring.go new file mode 100644 index 00000000000000..d627bc68903308 --- /dev/null +++ b/src/crypto/internal/fips140/boring.go @@ -0,0 +1,10 @@ +// Copyright 2024 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. + +// Keep in sync with notboring.go and crypto/internal/boring/boring.go. +//go:build boringcrypto && linux && (amd64 || arm64) && !android && !msan && cgo + +package fips140 + +const boringEnabled = true diff --git a/src/crypto/internal/fips140/check/check.go b/src/crypto/internal/fips140/check/check.go index ff61b80cb37ed2..454cd6c738b1af 100644 --- a/src/crypto/internal/fips140/check/check.go +++ b/src/crypto/internal/fips140/check/check.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package check implements the FIPS-140 load-time code+data verification. +// Package check implements the FIPS 140 load-time code+data verification. // Every FIPS package providing cryptographic functionality except hmac and sha256 // must import crypto/internal/fips140/check, so that the verification happens // before initialization of package global variables. @@ -13,37 +13,18 @@ package check import ( + "crypto/internal/fips140" "crypto/internal/fips140/hmac" "crypto/internal/fips140/sha256" "crypto/internal/fips140deps/byteorder" "crypto/internal/fips140deps/godebug" "io" - "runtime" "unsafe" ) -// Enabled reports whether verification was enabled. -// If Enabled returns true, then verification succeeded, -// because if it failed the binary would have panicked at init time. -func Enabled() bool { - return enabled -} - -var enabled bool // set when verification is enabled -var Verified bool // set when verification succeeds, for testing - -// Supported reports whether the current GOOS/GOARCH is Supported at all. -func Supported() bool { - // See cmd/internal/obj/fips.go's EnableFIPS for commentary. - switch { - case runtime.GOARCH == "wasm", - runtime.GOOS == "windows" && runtime.GOARCH == "386", - runtime.GOOS == "windows" && runtime.GOARCH == "arm", - runtime.GOOS == "aix": - return false - } - return true -} +// Verified is set when verification succeeded. It can be expected to always be +// true when [fips140.Enabled] is true, or init would have panicked. +var Verified bool // Linkinfo holds the go:fipsinfo symbol prepared by the linker. // See cmd/link/internal/ld/fips.go for details. @@ -71,32 +52,12 @@ const fipsMagic = " Go fipsinfo \xff\x00" var zeroSum [32]byte func init() { - v := godebug.Value("#fips140") - enabled = v != "" && v != "off" - if !enabled { + if !fips140.Enabled { return } - if asanEnabled { - // ASAN disapproves of reading swaths of global memory below. - // One option would be to expose runtime.asanunpoison through - // crypto/internal/fips140deps and then call it to unpoison the range - // before reading it, but it is unclear whether that would then cause - // false negatives. For now, FIPS+ASAN doesn't need to work. - // If this is made to work, also re-enable the test in check_test.go - // and in cmd/dist/test.go. - panic("fips140: cannot verify in asan mode") - } - - switch v { - case "on", "only", "debug": - // ok - default: - panic("fips140: unknown GODEBUG setting fips140=" + v) - } - - if !Supported() { - panic("fips140: unavailable on " + runtime.GOOS + "-" + runtime.GOARCH) + if err := fips140.Supported(); err != nil { + panic("fips140: " + err.Error()) } if Linkinfo.Magic[0] != 0xff || string(Linkinfo.Magic[1:]) != fipsMagic || Linkinfo.Sum == zeroSum { @@ -132,7 +93,14 @@ func init() { panic("fips140: verification mismatch") } - if v == "debug" { + // "The temporary value(s) generated during the integrity test of the + // module’s software or firmware shall [05.10] be zeroised from the module + // upon completion of the integrity test" + clear(sum) + clear(nbuf[:]) + h.Reset() + + if godebug.Value("fips140") == "debug" { println("fips140: verified code+data") } diff --git a/src/crypto/internal/fips140/drbg/rand.go b/src/crypto/internal/fips140/drbg/rand.go index 736a4b0cc0f4b3..c1a3ea0ae658ff 100644 --- a/src/crypto/internal/fips140/drbg/rand.go +++ b/src/crypto/internal/fips140/drbg/rand.go @@ -2,17 +2,30 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// Package drbg provides cryptographically secure random bytes +// usable by FIPS code. In FIPS mode it uses an SP 800-90A Rev. 1 +// Deterministic Random Bit Generator (DRBG). Otherwise, +// it uses the operating system's random number generator. package drbg import ( "crypto/internal/entropy" "crypto/internal/fips140" + "crypto/internal/randutil" "crypto/internal/sysrand" + "io" "sync" ) -var mu sync.Mutex -var drbg *Counter +var drbgs = sync.Pool{ + New: func() any { + var c *Counter + entropy.Depleted(func(seed *[48]byte) { + c = NewCounter(seed) + }) + return c + }, +} // Read fills b with cryptographically secure random bytes. In FIPS mode, it // uses an SP 800-90A Rev. 1 Deterministic Random Bit Generator (DRBG). @@ -31,14 +44,8 @@ func Read(b []byte) { additionalInput := new([SeedSize]byte) sysrand.Read(additionalInput[:16]) - mu.Lock() - defer mu.Unlock() - - if drbg == nil { - entropy.Depleted(func(seed *[48]byte) { - drbg = NewCounter(seed) - }) - } + drbg := drbgs.Get().(*Counter) + defer drbgs.Put(drbg) for len(b) > 0 { size := min(len(b), maxRequestSize) @@ -56,3 +63,38 @@ func Read(b []byte) { b = b[size:] } } + +// DefaultReader is a sentinel type, embedded in the default +// [crypto/rand.Reader], used to recognize it when passed to +// APIs that accept a rand io.Reader. +type DefaultReader interface{ defaultReader() } + +// ReadWithReader uses Reader to fill b with cryptographically secure random +// bytes. It is intended for use in APIs that expose a rand io.Reader. +// +// If Reader is not the default Reader from crypto/rand, +// [randutil.MaybeReadByte] and [fips140.RecordNonApproved] are called. +func ReadWithReader(r io.Reader, b []byte) error { + if _, ok := r.(DefaultReader); ok { + Read(b) + return nil + } + + fips140.RecordNonApproved() + randutil.MaybeReadByte(r) + _, err := io.ReadFull(r, b) + return err +} + +// ReadWithReaderDeterministic is like ReadWithReader, but it doesn't call +// [randutil.MaybeReadByte] on non-default Readers. +func ReadWithReaderDeterministic(r io.Reader, b []byte) error { + if _, ok := r.(DefaultReader); ok { + Read(b) + return nil + } + + fips140.RecordNonApproved() + _, err := io.ReadFull(r, b) + return err +} diff --git a/src/crypto/internal/fips140/drbg/rand_test.go b/src/crypto/internal/fips140/drbg/rand_test.go new file mode 100644 index 00000000000000..945ebde933e1bc --- /dev/null +++ b/src/crypto/internal/fips140/drbg/rand_test.go @@ -0,0 +1,27 @@ +// Copyright 2025 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. + +package drbg + +import ( + "crypto/internal/fips140" + "testing" +) + +func BenchmarkDBRG(b *testing.B) { + old := fips140.Enabled + defer func() { + fips140.Enabled = old + }() + fips140.Enabled = true + + const N = 64 + b.SetBytes(N) + b.RunParallel(func(pb *testing.PB) { + buf := make([]byte, N) + for pb.Next() { + Read(buf) + } + }) +} diff --git a/src/crypto/internal/fips140/ecdh/ecdh.go b/src/crypto/internal/fips140/ecdh/ecdh.go index 19a45c00db9ce9..bf71c75a92c6eb 100644 --- a/src/crypto/internal/fips140/ecdh/ecdh.go +++ b/src/crypto/internal/fips140/ecdh/ecdh.go @@ -10,7 +10,6 @@ import ( "crypto/internal/fips140/drbg" "crypto/internal/fips140/nistec" "crypto/internal/fips140deps/byteorder" - "crypto/internal/randutil" "errors" "io" "math/bits" @@ -137,8 +136,6 @@ var p521Order = []byte{0x01, 0xff, } // GenerateKey generates a new ECDSA private key pair for the specified curve. -// -// In FIPS mode, rand is ignored. func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) { fips140.RecordApproved() // This procedure is equivalent to Key Pair Generation by Testing @@ -146,18 +143,13 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) { for { key := make([]byte, len(c.N)) - if fips140.Enabled { - drbg.Read(key) - } else { - randutil.MaybeReadByte(rand) - if _, err := io.ReadFull(rand, key); err != nil { - return nil, err - } - // In tests, rand will return all zeros and NewPrivateKey will reject - // the zero key as it generates the identity as a public key. This also - // makes this function consistent with crypto/elliptic.GenerateKey. - key[1] ^= 0x42 + if err := drbg.ReadWithReader(rand, key); err != nil { + return nil, err } + // In tests, rand will return all zeros and NewPrivateKey will reject + // the zero key as it generates the identity as a public key. This also + // makes this function consistent with crypto/elliptic.GenerateKey. + key[1] ^= 0x42 // Mask off any excess bits if the size of the underlying field is not a // whole number of bytes, which is only the case for P-521. diff --git a/src/crypto/internal/fips140/ecdsa/cast.go b/src/crypto/internal/fips140/ecdsa/cast.go index a324cf929d8bf2..219b7211e74e92 100644 --- a/src/crypto/internal/fips140/ecdsa/cast.go +++ b/src/crypto/internal/fips140/ecdsa/cast.go @@ -54,7 +54,8 @@ func testHash() []byte { func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error { return fips140.PCT("ECDSA PCT", func() error { hash := testHash() - sig, err := Sign(c, sha512.New, k, nil, hash) + drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil) + sig, err := sign(c, k, drbg, hash) if err != nil { return err } diff --git a/src/crypto/internal/fips140/ecdsa/ecdsa.go b/src/crypto/internal/fips140/ecdsa/ecdsa.go index 61b40122a0fab4..11389e8210bc68 100644 --- a/src/crypto/internal/fips140/ecdsa/ecdsa.go +++ b/src/crypto/internal/fips140/ecdsa/ecdsa.go @@ -10,7 +10,6 @@ import ( "crypto/internal/fips140/bigmod" "crypto/internal/fips140/drbg" "crypto/internal/fips140/nistec" - "crypto/internal/randutil" "errors" "io" "sync" @@ -22,7 +21,7 @@ import ( type PrivateKey struct { pub PublicKey - d []byte // bigmod.(*Nat).Bytes output (fixed length) + d []byte // bigmod.(*Nat).Bytes output (same length as the curve order) } func (priv *PrivateKey) Bytes() []byte { @@ -187,20 +186,11 @@ func NewPublicKey[P Point[P]](c *Curve[P], Q []byte) (*PublicKey, error) { } // GenerateKey generates a new ECDSA private key pair for the specified curve. -// -// In FIPS mode, rand is ignored. func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) { fips140.RecordApproved() k, Q, err := randomPoint(c, func(b []byte) error { - if fips140.Enabled { - drbg.Read(b) - return nil - } else { - randutil.MaybeReadByte(rand) - _, err := io.ReadFull(rand, b) - return err - } + return drbg.ReadWithReader(rand, b) }) if err != nil { return nil, err @@ -272,7 +262,7 @@ func randomPoint[P Point[P]](c *Curve[P], generate func([]byte) error) (k *bigmo var testingOnlyRejectionSamplingLooped func() // Signature is an ECDSA signature, where r and s are represented as big-endian -// fixed-length byte slices. +// byte slices of the same length as the curve order. type Signature struct { R, S []byte } @@ -281,8 +271,6 @@ type Signature struct { // the hash function H) using the private key, priv. If the hash is longer than // the bit-length of the private key's curve order, the hash will be truncated // to that length. -// -// The signature is randomized. If FIPS mode is enabled, rand is ignored. func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey, rand io.Reader, hash []byte) (*Signature, error) { if priv.pub.curve != c.curve { return nil, errors.New("ecdsa: private key does not match curve") @@ -296,13 +284,8 @@ func Sign[P Point[P], H fips140.Hash](c *Curve[P], h func() H, priv *PrivateKey, // advantage of closely resembling Deterministic ECDSA. Z := make([]byte, len(priv.d)) - if fips140.Enabled { - drbg.Read(Z) - } else { - randutil.MaybeReadByte(rand) - if _, err := io.ReadFull(rand, Z); err != nil { - return nil, err - } + if err := drbg.ReadWithReader(rand, Z); err != nil { + return nil, err } // See https://github.com/cfrg/draft-irtf-cfrg-det-sigs-with-noise/issues/6 diff --git a/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go b/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go index 01379f998f10e2..d0a49cad610c25 100644 --- a/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go +++ b/src/crypto/internal/fips140/ecdsa/ecdsa_s390x.go @@ -47,15 +47,53 @@ func canUseKDSA(c curveID) (functionCode uint64, blockSize int, ok bool) { case p384: return 2, 48, true case p521: + // Note that the block size doesn't match the field size for P-521. return 3, 80, true } return 0, 0, false // A mismatch } -func hashToBytes[P Point[P]](c *Curve[P], dst, hash []byte) { +func hashToBytes[P Point[P]](c *Curve[P], hash []byte) []byte { e := bigmod.NewNat() hashToNat(c, e, hash) - copy(dst, e.Bytes(c.N)) + return e.Bytes(c.N) +} + +// randomScalar is a copy of [randomPoint] that doesn't call ScalarBaseMult. +func randomScalar[P Point[P]](c *Curve[P], generate func([]byte) error) (k *bigmod.Nat, err error) { + for { + b := make([]byte, c.N.Size()) + if err := generate(b); err != nil { + return nil, err + } + if excess := len(b)*8 - c.N.BitLen(); excess > 0 { + if c.curve != p521 { + panic("ecdsa: internal error: unexpectedly masking off bits") + } + b = rightShift(b, excess) + } + if k, err := bigmod.NewNat().SetBytes(b, c.N); err == nil && k.IsZero() == 0 { + return k, nil + } + } +} + +func appendBlock(p []byte, blocksize int, b []byte) []byte { + if len(b) > blocksize { + panic("ecdsa: internal error: appendBlock input larger than block") + } + padding := blocksize - len(b) + p = append(p, make([]byte, padding)...) + return append(p, b...) +} + +func trimBlock(p []byte, size int) ([]byte, error) { + for _, b := range p[:len(p)-size] { + if b != 0 { + return nil, errors.New("ecdsa: internal error: KDSA produced invalid signature") + } + } + return p[len(p)-size:], nil } func sign[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash []byte) (*Signature, error) { @@ -64,7 +102,7 @@ func sign[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash []byte return signGeneric(c, priv, drbg, hash) } for { - k, _, err := randomPoint(c, func(b []byte) error { + k, err := randomScalar(c, func(b []byte) error { drbg.Generate(b) return nil }) @@ -95,17 +133,27 @@ func sign[P Point[P]](c *Curve[P], priv *PrivateKey, drbg *hmacDRBG, hash []byte // Copy content into the parameter block. In the sign case, // we copy hashed message, private key and random number into - // the parameter block. - hashToBytes(c, params[2*blockSize:3*blockSize], hash) - copy(params[3*blockSize+blockSize-len(priv.d):], priv.d) - copy(params[4*blockSize:5*blockSize], k.Bytes(c.N)) + // the parameter block. We skip the signature slots. + p := params[:2*blockSize] + p = appendBlock(p, blockSize, hashToBytes(c, hash)) + p = appendBlock(p, blockSize, priv.d) + p = appendBlock(p, blockSize, k.Bytes(c.N)) // Convert verify function code into a sign function code by adding 8. // We also need to set the 'deterministic' bit in the function code, by // adding 128, in order to stop the instruction using its own random number // generator in addition to the random number we supply. switch kdsa(functionCode+136, ¶ms) { case 0: // success - return &Signature{R: params[:blockSize], S: params[blockSize : 2*blockSize]}, nil + elementSize := (c.N.BitLen() + 7) / 8 + r, err := trimBlock(params[:blockSize], elementSize) + if err != nil { + return nil, err + } + s, err := trimBlock(params[blockSize:2*blockSize], elementSize) + if err != nil { + return nil, err + } + return &Signature{R: r, S: s}, nil case 1: // error return nil, errors.New("zero parameter") case 2: // retry @@ -149,10 +197,12 @@ func verify[P Point[P]](c *Curve[P], pub *PublicKey, hash []byte, sig *Signature // Copy content into the parameter block. In the verify case, // we copy signature (r), signature(s), hashed message, public key x component, // and public key y component into the parameter block. - copy(params[0*blockSize+blockSize-len(r):], r) - copy(params[1*blockSize+blockSize-len(s):], s) - hashToBytes(c, params[2*blockSize:3*blockSize], hash) - copy(params[3*blockSize:5*blockSize], pub.q[1:]) // strip 0x04 prefix + p := params[:0] + p = appendBlock(p, blockSize, r) + p = appendBlock(p, blockSize, s) + p = appendBlock(p, blockSize, hashToBytes(c, hash)) + p = appendBlock(p, blockSize, pub.q[1:1+len(pub.q)/2]) + p = appendBlock(p, blockSize, pub.q[1+len(pub.q)/2:]) if kdsa(functionCode, ¶ms) != 0 { return errors.New("invalid signature") } diff --git a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go index 4f085e2801b79d..8f520911702492 100644 --- a/src/crypto/internal/fips140/ecdsa/hmacdrbg.go +++ b/src/crypto/internal/fips140/ecdsa/hmacdrbg.go @@ -116,6 +116,15 @@ func newDRBG[H fips140.Hash](hash func() H, entropy, nonce []byte, s personaliza return d } +// TestingOnlyNewDRBG creates an SP 800-90A Rev. 1 HMAC_DRBG with a plain +// personalization string. +// +// This should only be used for ACVP testing. hmacDRBG is not intended to be +// used directly. +func TestingOnlyNewDRBG(hash func() fips140.Hash, entropy, nonce []byte, s []byte) *hmacDRBG { + return newDRBG(hash, entropy, nonce, plainPersonalizationString(s)) +} + func pad000(h *hmac.HMAC, writtenSoFar int) { blockSize := h.BlockSize() if rem := writtenSoFar % blockSize; rem != 0 { diff --git a/src/crypto/internal/fips140/ed25519/ed25519.go b/src/crypto/internal/fips140/ed25519/ed25519.go index 9824cbdf814926..bbdc5b4a8ba2f9 100644 --- a/src/crypto/internal/fips140/ed25519/ed25519.go +++ b/src/crypto/internal/fips140/ed25519/ed25519.go @@ -11,7 +11,6 @@ import ( "crypto/internal/fips140/edwards25519" "crypto/internal/fips140/sha512" "errors" - "io" "strconv" ) @@ -61,24 +60,14 @@ func (pub *PublicKey) Bytes() []byte { } // GenerateKey generates a new Ed25519 private key pair. -// -// In FIPS mode, rand is ignored. Otherwise, the output of this function is -// deterministic, and equivalent to reading 32 bytes from rand, and passing them -// to [NewKeyFromSeed]. -func GenerateKey(rand io.Reader) (*PrivateKey, error) { +func GenerateKey() (*PrivateKey, error) { priv := &PrivateKey{} - return generateKey(priv, rand) + return generateKey(priv) } -func generateKey(priv *PrivateKey, rand io.Reader) (*PrivateKey, error) { +func generateKey(priv *PrivateKey) (*PrivateKey, error) { fips140.RecordApproved() - if fips140.Enabled { - drbg.Read(priv.seed[:]) - } else { - if _, err := io.ReadFull(rand, priv.seed[:]); err != nil { - return nil, err - } - } + drbg.Read(priv.seed[:]) precomputePrivateKey(priv) if err := fipsPCT(priv); err != nil { // This clearly can't happen, but FIPS 140-3 requires that we check. diff --git a/src/crypto/internal/fips140/edwards25519/field/fe.go b/src/crypto/internal/fips140/edwards25519/field/fe.go index 2d76ba72740b80..21bedefa0ca7d3 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe.go @@ -233,18 +233,22 @@ func (v *Element) bytes(out *[32]byte) []byte { t := *v t.reduce() - var buf [8]byte - for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} { - bitsOffset := i * 51 - byteorder.LEPutUint64(buf[:], l<= len(out) { - break - } - out[off] |= bb - } - } + // Pack five 51-bit limbs into four 64-bit words: + // + // 255 204 153 102 51 0 + // ├──l4──┼──l3──┼──l2──┼──l1──┼──l0──┤ + // ├───u3───┼───u2───┼───u1───┼───u0───┤ + // 256 192 128 64 0 + + u0 := t.l1<<51 | t.l0 + u1 := t.l2<<(102-64) | t.l1>>(64-51) + u2 := t.l3<<(153-128) | t.l2>>(128-102) + u3 := t.l4<<(204-192) | t.l3>>(192-153) + + byteorder.LEPutUint64(out[0*8:], u0) + byteorder.LEPutUint64(out[1*8:], u1) + byteorder.LEPutUint64(out[2*8:], u2) + byteorder.LEPutUint64(out[3*8:], u3) return out[:] } diff --git a/src/crypto/internal/fips140/edwards25519/field/fe_bench_test.go b/src/crypto/internal/fips140/edwards25519/field/fe_bench_test.go index 84fdf05a8e474a..fb80ca88fe255d 100644 --- a/src/crypto/internal/fips140/edwards25519/field/fe_bench_test.go +++ b/src/crypto/internal/fips140/edwards25519/field/fe_bench_test.go @@ -47,3 +47,11 @@ func BenchmarkMult32(b *testing.B) { x.Mult32(x, 0xaa42aa42) } } + +func BenchmarkBytes(b *testing.B) { + x := new(Element).One() + b.ResetTimer() + for i := 0; i < b.N; i++ { + x.Bytes() + } +} diff --git a/src/crypto/internal/fips140/edwards25519/scalar.go b/src/crypto/internal/fips140/edwards25519/scalar.go index 9d60146d794d68..22bbebfbb41d7d 100644 --- a/src/crypto/internal/fips140/edwards25519/scalar.go +++ b/src/crypto/internal/fips140/edwards25519/scalar.go @@ -7,6 +7,7 @@ package edwards25519 import ( "crypto/internal/fips140deps/byteorder" "errors" + "math/bits" ) // A Scalar is an integer modulo @@ -179,15 +180,23 @@ func isReduced(s []byte) bool { return false } - for i := len(s) - 1; i >= 0; i-- { - switch { - case s[i] > scalarMinusOneBytes[i]: - return false - case s[i] < scalarMinusOneBytes[i]: - return true - } - } - return true + s0 := byteorder.LEUint64(s[:8]) + s1 := byteorder.LEUint64(s[8:16]) + s2 := byteorder.LEUint64(s[16:24]) + s3 := byteorder.LEUint64(s[24:]) + + l0 := byteorder.LEUint64(scalarMinusOneBytes[:8]) + l1 := byteorder.LEUint64(scalarMinusOneBytes[8:16]) + l2 := byteorder.LEUint64(scalarMinusOneBytes[16:24]) + l3 := byteorder.LEUint64(scalarMinusOneBytes[24:]) + + // Do a constant time subtraction chain scalarMinusOneBytes - s. If there is + // a borrow at the end, then s > scalarMinusOneBytes. + _, b := bits.Sub64(l0, s0, 0) + _, b = bits.Sub64(l1, s1, b) + _, b = bits.Sub64(l2, s2, b) + _, b = bits.Sub64(l3, s3, b) + return b == 0 } // SetBytesWithClamping applies the buffer pruning described in RFC 8032, diff --git a/src/crypto/internal/fips140/edwards25519/scalar_test.go b/src/crypto/internal/fips140/edwards25519/scalar_test.go index 05551ef771187b..76e920a2feb2e2 100644 --- a/src/crypto/internal/fips140/edwards25519/scalar_test.go +++ b/src/crypto/internal/fips140/edwards25519/scalar_test.go @@ -26,7 +26,7 @@ func quickCheckConfig(slowScale int) *quick.Config { var scOneBytes = [32]byte{1} var scOne, _ = new(Scalar).SetCanonicalBytes(scOneBytes[:]) -var scMinusOne, _ = new(Scalar).SetCanonicalBytes(scalarMinusOneBytes[:]) +var scMinusOne = new(Scalar).Subtract(new(Scalar), scOne) // Generate returns a valid (reduced modulo l) Scalar with a distribution // weighted towards high, low, and edge values. @@ -38,7 +38,7 @@ func (Scalar) Generate(rand *mathrand.Rand, size int) reflect.Value { case diceRoll == 1: s = scOneBytes case diceRoll == 2: - s = scalarMinusOneBytes + s = [32]byte(scMinusOne.Bytes()) case diceRoll < 5: // Generate a low scalar in [0, 2^125). rand.Read(s[:16]) @@ -96,16 +96,29 @@ func TestScalarSetCanonicalBytes(t *testing.T) { t.Errorf("failed scalar->bytes->scalar round-trip: %v", err) } - b := scalarMinusOneBytes - b[31] += 1 - s := scOne - if out, err := s.SetCanonicalBytes(b[:]); err == nil { - t.Errorf("SetCanonicalBytes worked on a non-canonical value") - } else if s != scOne { - t.Errorf("SetCanonicalBytes modified its receiver") - } else if out != nil { - t.Errorf("SetCanonicalBytes did not return nil with an error") + expectReject := func(b []byte) { + t.Helper() + s := scOne + if out, err := s.SetCanonicalBytes(b[:]); err == nil { + t.Errorf("SetCanonicalBytes worked on a non-canonical value") + } else if s != scOne { + t.Errorf("SetCanonicalBytes modified its receiver") + } else if out != nil { + t.Errorf("SetCanonicalBytes did not return nil with an error") + } } + + b := scMinusOne.Bytes() + b[0] += 1 + expectReject(b) + + b = scMinusOne.Bytes() + b[31] += 1 + expectReject(b) + + b = scMinusOne.Bytes() + b[31] |= 0b1000_0000 + expectReject(b) } func TestScalarSetUniformBytes(t *testing.T) { diff --git a/src/crypto/internal/fips140/fips140.go b/src/crypto/internal/fips140/fips140.go index cec9d13e35bb82..c7b167b82a1412 100644 --- a/src/crypto/internal/fips140/fips140.go +++ b/src/crypto/internal/fips140/fips140.go @@ -4,18 +4,64 @@ package fips140 -import "crypto/internal/fips140deps/godebug" +import ( + "crypto/internal/fips140deps/godebug" + "errors" + "runtime" +) var Enabled bool var debug bool func init() { - switch godebug.Value("#fips140") { + v := godebug.Value("#fips140") + switch v { case "on", "only": Enabled = true case "debug": Enabled = true debug = true + case "off", "": + default: + panic("fips140: unknown GODEBUG setting fips140=" + v) } } + +// Supported returns an error if FIPS 140-3 mode can't be enabled. +func Supported() error { + // Keep this in sync with fipsSupported in cmd/dist/test.go. + + // ASAN disapproves of reading swaths of global memory in fips140/check. + // One option would be to expose runtime.asanunpoison through + // crypto/internal/fips140deps and then call it to unpoison the range + // before reading it, but it is unclear whether that would then cause + // false negatives. For now, FIPS+ASAN doesn't need to work. + if asanEnabled { + return errors.New("FIPS 140-3 mode is incompatible with ASAN") + } + + // See EnableFIPS in cmd/internal/obj/fips.go for commentary. + switch { + case runtime.GOARCH == "wasm", + runtime.GOOS == "windows" && runtime.GOARCH == "386", + runtime.GOOS == "windows" && runtime.GOARCH == "arm", + runtime.GOOS == "openbsd", // due to -fexecute-only, see #70880 + runtime.GOOS == "aix": + return errors.New("FIPS 140-3 mode is not supported on " + runtime.GOOS + "-" + runtime.GOARCH) + } + + if boringEnabled { + return errors.New("FIPS 140-3 mode is incompatible with GOEXPERIMENT=boringcrypto") + } + + return nil +} + +func Name() string { + return "Go Cryptographic Module" +} + +func Version() string { + return "v1.0" +} diff --git a/src/crypto/internal/fips140/mlkem/cast.go b/src/crypto/internal/fips140/mlkem/cast.go index d3ae84ec3f1122..a432d1fdab0e2b 100644 --- a/src/crypto/internal/fips140/mlkem/cast.go +++ b/src/crypto/internal/fips140/mlkem/cast.go @@ -40,7 +40,7 @@ func init() { dk := &DecapsulationKey768{} kemKeyGen(dk, d, z) ek := dk.EncapsulationKey() - c, Ke := ek.EncapsulateInternal(m) + Ke, c := ek.EncapsulateInternal(m) Kd, err := dk.Decapsulate(c) if err != nil { return err diff --git a/src/crypto/internal/fips140/mlkem/generate1024.go b/src/crypto/internal/fips140/mlkem/generate1024.go index e002bf1414ae96..9e38ad00df99e0 100644 --- a/src/crypto/internal/fips140/mlkem/generate1024.go +++ b/src/crypto/internal/fips140/mlkem/generate1024.go @@ -22,6 +22,7 @@ var replacements = map[string]string{ "CiphertextSize768": "CiphertextSize1024", "EncapsulationKeySize768": "EncapsulationKeySize1024", + "decapsulationKeySize768": "decapsulationKeySize1024", "encryptionKey": "encryptionKey1024", "decryptionKey": "decryptionKey1024", @@ -33,9 +34,11 @@ var replacements = map[string]string{ "kemEncaps": "kemEncaps1024", "pkeEncrypt": "pkeEncrypt1024", - "DecapsulationKey768": "DecapsulationKey1024", - "NewDecapsulationKey768": "NewDecapsulationKey1024", - "newKeyFromSeed": "newKeyFromSeed1024", + "DecapsulationKey768": "DecapsulationKey1024", + "NewDecapsulationKey768": "NewDecapsulationKey1024", + "TestingOnlyNewDecapsulationKey768": "TestingOnlyNewDecapsulationKey1024", + "newKeyFromSeed": "newKeyFromSeed1024", + "TestingOnlyExpandedBytes768": "TestingOnlyExpandedBytes1024", "kemDecaps": "kemDecaps1024", "pkeDecrypt": "pkeDecrypt1024", diff --git a/src/crypto/internal/fips140/mlkem/mlkem1024.go b/src/crypto/internal/fips140/mlkem/mlkem1024.go index 5aa3c69243b346..034bf3b5d6682d 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem1024.go +++ b/src/crypto/internal/fips140/mlkem/mlkem1024.go @@ -3,6 +3,7 @@ package mlkem import ( + "bytes" "crypto/internal/fips140" "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha3" @@ -33,6 +34,32 @@ func (dk *DecapsulationKey1024) Bytes() []byte { return b[:] } +// TestingOnlyExpandedBytes1024 returns the decapsulation key as a byte slice +// using the full expanded NIST encoding. +// +// This should only be used for ACVP testing. For all other purposes prefer +// the Bytes method that returns the (much smaller) seed. +func TestingOnlyExpandedBytes1024(dk *DecapsulationKey1024) []byte { + b := make([]byte, 0, decapsulationKeySize1024) + + // ByteEncode₁₂(s) + for i := range dk.s { + b = polyByteEncode(b, dk.s[i]) + } + + // ByteEncode₁₂(t) || ρ + for i := range dk.t { + b = polyByteEncode(b, dk.t[i]) + } + b = append(b, dk.ρ[:]...) + + // H(ek) || z + b = append(b, dk.h[:]...) + b = append(b, dk.z[:]...) + + return b +} + // EncapsulationKey returns the public encapsulation key necessary to produce // ciphertexts. func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { @@ -130,6 +157,53 @@ func newKeyFromSeed1024(dk *DecapsulationKey1024, seed []byte) (*DecapsulationKe return dk, nil } +// TestingOnlyNewDecapsulationKey1024 parses a decapsulation key from its expanded NIST format. +// +// Bytes() must not be called on the returned key, as it will not produce the +// original seed. +// +// This function should only be used for ACVP testing. Prefer NewDecapsulationKey1024 for all +// other purposes. +func TestingOnlyNewDecapsulationKey1024(b []byte) (*DecapsulationKey1024, error) { + if len(b) != decapsulationKeySize1024 { + return nil, errors.New("mlkem: invalid NIST decapsulation key length") + } + + dk := &DecapsulationKey1024{} + for i := range dk.s { + var err error + dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12]) + if err != nil { + return nil, errors.New("mlkem: invalid secret key encoding") + } + b = b[encodingSize12:] + } + + ek, err := NewEncapsulationKey1024(b[:EncapsulationKeySize1024]) + if err != nil { + return nil, err + } + dk.ρ = ek.ρ + dk.h = ek.h + dk.encryptionKey1024 = ek.encryptionKey1024 + b = b[EncapsulationKeySize1024:] + + if !bytes.Equal(dk.h[:], b[:32]) { + return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes") + } + b = b[32:] + + copy(dk.z[:], b) + + // Generate a random d value for use in Bytes(). This is a safety mechanism + // that avoids returning a broken key vs a random key if this function is + // called in contravention of the TestingOnlyNewDecapsulationKey1024 function + // comment advising against it. + drbg.Read(dk.d[:]) + + return dk, nil +} + // kemKeyGen1024 generates a decapsulation key. // // It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and @@ -189,7 +263,7 @@ func kemKeyGen1024(dk *DecapsulationKey1024, d, z *[32]byte) { // the first operational use (if not exported before the first use)." func kemPCT1024(dk *DecapsulationKey1024) error { ek := dk.EncapsulationKey() - c, K := ek.Encapsulate() + K, c := ek.Encapsulate() K1, err := dk.Decapsulate(c) if err != nil { return err @@ -204,13 +278,13 @@ func kemPCT1024(dk *DecapsulationKey1024) error { // encapsulation key, drawing random bytes from a DRBG. // // The shared key must be kept secret. -func (ek *EncapsulationKey1024) Encapsulate() (ciphertext, sharedKey []byte) { +func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) { // The actual logic is in a separate function to outline this allocation. var cc [CiphertextSize1024]byte return ek.encapsulate(&cc) } -func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (ciphertext, sharedKey []byte) { +func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (sharedKey, ciphertext []byte) { var m [messageSize]byte drbg.Read(m[:]) // Note that the modulus check (step 2 of the encapsulation key check from @@ -221,7 +295,7 @@ func (ek *EncapsulationKey1024) encapsulate(cc *[CiphertextSize1024]byte) (ciphe // EncapsulateInternal is a derandomized version of Encapsulate, exclusively for // use in tests. -func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (ciphertext, sharedKey []byte) { +func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) { cc := &[CiphertextSize1024]byte{} return kemEncaps1024(cc, ek, m) } @@ -229,14 +303,14 @@ func (ek *EncapsulationKey1024) EncapsulateInternal(m *[32]byte) (ciphertext, sh // kemEncaps1024 generates a shared key and an associated ciphertext. // // It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17. -func kemEncaps1024(cc *[CiphertextSize1024]byte, ek *EncapsulationKey1024, m *[messageSize]byte) (c, K []byte) { +func kemEncaps1024(cc *[CiphertextSize1024]byte, ek *EncapsulationKey1024, m *[messageSize]byte) (K, c []byte) { g := sha3.New512() g.Write(m[:]) g.Write(ek.h[:]) G := g.Sum(nil) K, r := G[:SharedKeySize], G[SharedKeySize:] c = pkeEncrypt1024(cc, &ek.encryptionKey1024, m, r) - return c, K + return K, c } // NewEncapsulationKey1024 parses an encapsulation key from its encoded form. diff --git a/src/crypto/internal/fips140/mlkem/mlkem768.go b/src/crypto/internal/fips140/mlkem/mlkem768.go index 0c91ceadc4284e..77043830d4d962 100644 --- a/src/crypto/internal/fips140/mlkem/mlkem768.go +++ b/src/crypto/internal/fips140/mlkem/mlkem768.go @@ -24,6 +24,7 @@ package mlkem //go:generate go run generate1024.go -input mlkem768.go -output mlkem1024.go import ( + "bytes" "crypto/internal/fips140" "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha3" @@ -57,6 +58,7 @@ const ( CiphertextSize768 = k*encodingSize10 + encodingSize4 EncapsulationKeySize768 = k*encodingSize12 + 32 + decapsulationKeySize768 = k*encodingSize12 + EncapsulationKeySize768 + 32 + 32 ) // ML-KEM-1024 parameters. @@ -65,6 +67,7 @@ const ( CiphertextSize1024 = k1024*encodingSize11 + encodingSize5 EncapsulationKeySize1024 = k1024*encodingSize12 + 32 + decapsulationKeySize1024 = k1024*encodingSize12 + EncapsulationKeySize1024 + 32 + 32 ) // A DecapsulationKey768 is the secret key used to decapsulate a shared key from a @@ -90,6 +93,32 @@ func (dk *DecapsulationKey768) Bytes() []byte { return b[:] } +// TestingOnlyExpandedBytes768 returns the decapsulation key as a byte slice +// using the full expanded NIST encoding. +// +// This should only be used for ACVP testing. For all other purposes prefer +// the Bytes method that returns the (much smaller) seed. +func TestingOnlyExpandedBytes768(dk *DecapsulationKey768) []byte { + b := make([]byte, 0, decapsulationKeySize768) + + // ByteEncode₁₂(s) + for i := range dk.s { + b = polyByteEncode(b, dk.s[i]) + } + + // ByteEncode₁₂(t) || ρ + for i := range dk.t { + b = polyByteEncode(b, dk.t[i]) + } + b = append(b, dk.ρ[:]...) + + // H(ek) || z + b = append(b, dk.h[:]...) + b = append(b, dk.z[:]...) + + return b +} + // EncapsulationKey returns the public encapsulation key necessary to produce // ciphertexts. func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { @@ -187,6 +216,53 @@ func newKeyFromSeed(dk *DecapsulationKey768, seed []byte) (*DecapsulationKey768, return dk, nil } +// TestingOnlyNewDecapsulationKey768 parses a decapsulation key from its expanded NIST format. +// +// Bytes() must not be called on the returned key, as it will not produce the +// original seed. +// +// This function should only be used for ACVP testing. Prefer NewDecapsulationKey768 for all +// other purposes. +func TestingOnlyNewDecapsulationKey768(b []byte) (*DecapsulationKey768, error) { + if len(b) != decapsulationKeySize768 { + return nil, errors.New("mlkem: invalid NIST decapsulation key length") + } + + dk := &DecapsulationKey768{} + for i := range dk.s { + var err error + dk.s[i], err = polyByteDecode[nttElement](b[:encodingSize12]) + if err != nil { + return nil, errors.New("mlkem: invalid secret key encoding") + } + b = b[encodingSize12:] + } + + ek, err := NewEncapsulationKey768(b[:EncapsulationKeySize768]) + if err != nil { + return nil, err + } + dk.ρ = ek.ρ + dk.h = ek.h + dk.encryptionKey = ek.encryptionKey + b = b[EncapsulationKeySize768:] + + if !bytes.Equal(dk.h[:], b[:32]) { + return nil, errors.New("mlkem: inconsistent H(ek) in encoded bytes") + } + b = b[32:] + + copy(dk.z[:], b) + + // Generate a random d value for use in Bytes(). This is a safety mechanism + // that avoids returning a broken key vs a random key if this function is + // called in contravention of the TestingOnlyNewDecapsulationKey768 function + // comment advising against it. + drbg.Read(dk.d[:]) + + return dk, nil +} + // kemKeyGen generates a decapsulation key. // // It implements ML-KEM.KeyGen_internal according to FIPS 203, Algorithm 16, and @@ -246,7 +322,7 @@ func kemKeyGen(dk *DecapsulationKey768, d, z *[32]byte) { // the first operational use (if not exported before the first use)." func kemPCT(dk *DecapsulationKey768) error { ek := dk.EncapsulationKey() - c, K := ek.Encapsulate() + K, c := ek.Encapsulate() K1, err := dk.Decapsulate(c) if err != nil { return err @@ -261,13 +337,13 @@ func kemPCT(dk *DecapsulationKey768) error { // encapsulation key, drawing random bytes from a DRBG. // // The shared key must be kept secret. -func (ek *EncapsulationKey768) Encapsulate() (ciphertext, sharedKey []byte) { +func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) { // The actual logic is in a separate function to outline this allocation. var cc [CiphertextSize768]byte return ek.encapsulate(&cc) } -func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (ciphertext, sharedKey []byte) { +func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (sharedKey, ciphertext []byte) { var m [messageSize]byte drbg.Read(m[:]) // Note that the modulus check (step 2 of the encapsulation key check from @@ -278,7 +354,7 @@ func (ek *EncapsulationKey768) encapsulate(cc *[CiphertextSize768]byte) (ciphert // EncapsulateInternal is a derandomized version of Encapsulate, exclusively for // use in tests. -func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (ciphertext, sharedKey []byte) { +func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (sharedKey, ciphertext []byte) { cc := &[CiphertextSize768]byte{} return kemEncaps(cc, ek, m) } @@ -286,14 +362,14 @@ func (ek *EncapsulationKey768) EncapsulateInternal(m *[32]byte) (ciphertext, sha // kemEncaps generates a shared key and an associated ciphertext. // // It implements ML-KEM.Encaps_internal according to FIPS 203, Algorithm 17. -func kemEncaps(cc *[CiphertextSize768]byte, ek *EncapsulationKey768, m *[messageSize]byte) (c, K []byte) { +func kemEncaps(cc *[CiphertextSize768]byte, ek *EncapsulationKey768, m *[messageSize]byte) (K, c []byte) { g := sha3.New512() g.Write(m[:]) g.Write(ek.h[:]) G := g.Sum(nil) K, r := G[:SharedKeySize], G[SharedKeySize:] c = pkeEncrypt(cc, &ek.encryptionKey, m, r) - return c, K + return K, c } // NewEncapsulationKey768 parses an encapsulation key from its encoded form. diff --git a/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s b/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s index 7c46b268ef7571..7efaa6ac187ad7 100644 --- a/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s +++ b/src/crypto/internal/fips140/nistec/p256_asm_ppc64le.s @@ -126,14 +126,23 @@ GLOBL p256mul<>(SB), 8, $160 #define PH V31 #define CAR1 V6 + +#define SEL V8 +#define ZER V9 + // func p256NegCond(val *p256Point, cond int) TEXT ·p256NegCond(SB), NOSPLIT, $0-16 MOVD val+0(FP), P1ptr MOVD $16, R16 - MOVD cond+8(FP), R6 - CMP $0, R6 - BC 12, 2, LR // just return if cond == 0 + // Copy cond into SEL (cond is R1 + 8 (cond offset) + 32) + MOVD $40, R17 + LXVDSX (R1)(R17), SEL + // Zeroize ZER + VSPLTISB $0, ZER + // SEL controls whether to return the original value (Y1H/Y1L) + // or the negated value (T1H/T1L). + VCMPEQUD SEL, ZER, SEL MOVD $p256mul<>+0x00(SB), CPOOL @@ -150,6 +159,9 @@ TEXT ·p256NegCond(SB), NOSPLIT, $0-16 VSUBUQM PL, Y1L, T1L // subtract part2 giving result VSUBEUQM PH, Y1H, CAR1, T1H // subtract part1 using carry from part2 + VSEL T1H, Y1H, SEL, T1H + VSEL T1L, Y1L, SEL, T1L + XXPERMDI T1H, T1H, $2, T1H XXPERMDI T1L, T1L, $2, T1L @@ -166,6 +178,8 @@ TEXT ·p256NegCond(SB), NOSPLIT, $0-16 #undef PL #undef PH #undef CAR1 +#undef SEL +#undef ZER #define P3ptr R3 #define P1ptr R4 diff --git a/src/crypto/internal/fips140/check/noasan.go b/src/crypto/internal/fips140/notasan.go similarity index 92% rename from src/crypto/internal/fips140/check/noasan.go rename to src/crypto/internal/fips140/notasan.go index 876d726f989096..639d419ef9c7b1 100644 --- a/src/crypto/internal/fips140/check/noasan.go +++ b/src/crypto/internal/fips140/notasan.go @@ -4,6 +4,6 @@ //go:build !asan -package check +package fips140 const asanEnabled = false diff --git a/src/crypto/internal/fips140/subtle/xor_loong64.go b/src/crypto/internal/fips140/notboring.go similarity index 55% rename from src/crypto/internal/fips140/subtle/xor_loong64.go rename to src/crypto/internal/fips140/notboring.go index e49f0fc9e3fa02..681521c687c7c6 100644 --- a/src/crypto/internal/fips140/subtle/xor_loong64.go +++ b/src/crypto/internal/fips140/notboring.go @@ -2,9 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build !purego +//go:build !(boringcrypto && linux && (amd64 || arm64) && !android && !msan && cgo) -package subtle +package fips140 -//go:noescape -func xorBytes(dst, a, b *byte, n int) +const boringEnabled = false diff --git a/src/crypto/internal/fips140/pbkdf2/pbkdf2.go b/src/crypto/internal/fips140/pbkdf2/pbkdf2.go index 8f6d9915041d3f..05923f68262ac6 100644 --- a/src/crypto/internal/fips140/pbkdf2/pbkdf2.go +++ b/src/crypto/internal/fips140/pbkdf2/pbkdf2.go @@ -7,15 +7,33 @@ package pbkdf2 import ( "crypto/internal/fips140" "crypto/internal/fips140/hmac" + "errors" ) +// divRoundUp divides x+y-1 by y, rounding up if the result is not whole. +// This function casts x and y to int64 in order to avoid cases where +// x+y would overflow int on systems where int is an int32. The result +// is an int, which is safe as (x+y-1)/y should always fit, regardless +// of the integer size. +func divRoundUp(x, y int) int { + return int((int64(x) + int64(y) - 1) / int64(y)) +} + func Key[Hash fips140.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) { setServiceIndicator(salt, keyLength) + if keyLength <= 0 { + return nil, errors.New("pkbdf2: keyLength must be larger than 0") + } + prf := hmac.New(h, []byte(password)) hmac.MarkAsUsedInKDF(prf) hashLen := prf.Size() - numBlocks := (keyLength + hashLen - 1) / hashLen + numBlocks := divRoundUp(keyLength, hashLen) + const maxBlocks = int64(1<<32 - 1) + if keyLength+hashLen < keyLength || int64(numBlocks) > maxBlocks { + return nil, errors.New("pbkdf2: keyLength too long") + } var buf [4]byte dk := make([]byte, 0, numBlocks*hashLen) diff --git a/src/crypto/internal/fips140/rsa/cast.go b/src/crypto/internal/fips140/rsa/cast.go index ec7b5f3aeb9275..b900b32c888641 100644 --- a/src/crypto/internal/fips140/rsa/cast.go +++ b/src/crypto/internal/fips140/rsa/cast.go @@ -171,6 +171,7 @@ func testPrivateKey() *PrivateKey { N: N, E: 65537, }, d: d, p: p, q: q, qInv: qInv, dP: dP, dQ: dQ, + fipsApproved: true, } } diff --git a/src/crypto/internal/fips140/rsa/keygen.go b/src/crypto/internal/fips140/rsa/keygen.go index a9e12eb1e8e920..c8314e78df663d 100644 --- a/src/crypto/internal/fips140/rsa/keygen.go +++ b/src/crypto/internal/fips140/rsa/keygen.go @@ -8,17 +8,14 @@ import ( "crypto/internal/fips140" "crypto/internal/fips140/bigmod" "crypto/internal/fips140/drbg" - "crypto/internal/randutil" "errors" "io" ) // GenerateKey generates a new RSA key pair of the given bit size. -// bits must be at least 128. -// -// When operating in FIPS mode, rand is ignored. +// bits must be at least 32. func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) { - if bits < 128 { + if bits < 32 { return nil, errors.New("rsa: key too small") } fips140.RecordApproved() @@ -57,23 +54,42 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) { return nil, errors.New("rsa: internal error: modulus size incorrect") } - φ, err := bigmod.NewModulusProduct(P.Nat().SubOne(P).Bytes(P), - Q.Nat().SubOne(Q).Bytes(Q)) + // d can be safely computed as e⁻¹ mod φ(N) where φ(N) = (p-1)(q-1), and + // indeed that's what both the original RSA paper and the pre-FIPS + // crypto/rsa implementation did. + // + // However, FIPS 186-5, A.1.1(3) requires computing it as e⁻¹ mod λ(N) + // where λ(N) = lcm(p-1, q-1). + // + // This makes d smaller by 1.5 bits on average, which is irrelevant both + // because we exclusively use the CRT for private operations and because + // we use constant time windowed exponentiation. On the other hand, it + // requires computing a GCD of two values that are not coprime, and then + // a division, both complex variable-time operations. + λ, err := totient(P, Q) + if err == errDivisorTooLarge { + // The divisor is too large, try again with different primes. + continue + } if err != nil { return nil, err } e := bigmod.NewNat().SetUint(65537) - d, ok := bigmod.NewNat().InverseVarTime(e, φ) + d, ok := bigmod.NewNat().InverseVarTime(e, λ) if !ok { - // This checks that GCD(e, (p-1)(q-1)) = 1, which is equivalent + // This checks that GCD(e, lcm(p-1, q-1)) = 1, which is equivalent // to checking GCD(e, p-1) = 1 and GCD(e, q-1) = 1 separately in // FIPS 186-5, Appendix A.1.3, steps 4.5 and 5.6. + // + // We waste a prime by retrying the whole process, since 65537 is + // probably only a factor of one of p-1 or q-1, but the probability + // of this check failing is only 1/65537, so it doesn't matter. continue } - if e.ExpandFor(φ).Mul(d, φ).IsOne() == 0 { - return nil, errors.New("rsa: internal error: e*d != 1 mod φ(N)") + if e.ExpandFor(λ).Mul(d, λ).IsOne() == 0 { + return nil, errors.New("rsa: internal error: e*d != 1 mod λ(N)") } // FIPS 186-5, A.1.1(3) requires checking that d > 2^(nlen / 2). @@ -93,32 +109,75 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) { } } +// errDivisorTooLarge is returned by [totient] when gcd(p-1, q-1) is too large. +var errDivisorTooLarge = errors.New("divisor too large") + +// totient computes the Carmichael totient function λ(N) = lcm(p-1, q-1). +func totient(p, q *bigmod.Modulus) (*bigmod.Modulus, error) { + a, b := p.Nat().SubOne(p), q.Nat().SubOne(q) + + // lcm(a, b) = a×b / gcd(a, b) = a × (b / gcd(a, b)) + + // Our GCD requires at least one of the numbers to be odd. For LCM we only + // need to preserve the larger prime power of each prime factor, so we can + // right-shift the number with the fewest trailing zeros until it's odd. + // For odd a, b and m >= n, lcm(a×2ᵐ, b×2ⁿ) = lcm(a×2ᵐ, b). + az, bz := a.TrailingZeroBitsVarTime(), b.TrailingZeroBitsVarTime() + if az < bz { + a = a.ShiftRightVarTime(az) + } else { + b = b.ShiftRightVarTime(bz) + } + + gcd, err := bigmod.NewNat().GCDVarTime(a, b) + if err != nil { + return nil, err + } + if gcd.IsOdd() == 0 { + return nil, errors.New("rsa: internal error: gcd(a, b) is even") + } + + // To avoid implementing multiple-precision division, we just try again if + // the divisor doesn't fit in a single word. This would have a chance of + // 2⁻⁶⁴ on 64-bit platforms, and 2⁻³² on 32-bit platforms, but testing 2⁻⁶⁴ + // edge cases is impractical, and we'd rather not behave differently on + // different platforms, so we reject divisors above 2³²-1. + if gcd.BitLenVarTime() > 32 { + return nil, errDivisorTooLarge + } + if gcd.IsZero() == 1 || gcd.Bits()[0] == 0 { + return nil, errors.New("rsa: internal error: gcd(a, b) is zero") + } + if rem := b.DivShortVarTime(gcd.Bits()[0]); rem != 0 { + return nil, errors.New("rsa: internal error: b is not divisible by gcd(a, b)") + } + + return bigmod.NewModulusProduct(a.Bytes(p), b.Bytes(q)) +} + // randomPrime returns a random prime number of the given bit size following -// the process in FIPS 186-5, Appendix A.1.3. rand is ignored in FIPS mode. +// the process in FIPS 186-5, Appendix A.1.3. func randomPrime(rand io.Reader, bits int) ([]byte, error) { - if bits < 64 { - return nil, errors.New("rsa: prime size must be at least 32-bit") + if bits < 16 { + return nil, errors.New("rsa: prime size must be at least 16 bits") } b := make([]byte, (bits+7)/8) for { - if fips140.Enabled { - drbg.Read(b) - } else { - randutil.MaybeReadByte(rand) - if _, err := io.ReadFull(rand, b); err != nil { - return nil, err - } - } - if excess := len(b)*8 - bits; excess != 0 { - b[0] >>= excess + if err := drbg.ReadWithReader(rand, b); err != nil { + return nil, err } + // Clear the most significant bits to reach the desired size. We use a + // mask rather than right-shifting b[0] to make it easier to inject test + // candidates, which can be represented as simple big-endian integers. + excess := len(b)*8 - bits + b[0] &= 0b1111_1111 >> excess // Don't let the value be too small: set the most significant two bits. // Setting the top two bits, rather than just the top bit, means that // when two of these values are multiplied together, the result isn't // ever one bit short. - if excess := len(b)*8 - bits; excess < 7 { + if excess < 7 { b[0] |= 0b1100_0000 >> excess } else { b[0] |= 0b0000_0001 @@ -213,9 +272,8 @@ func isPrime(w []byte) bool { b := make([]byte, (bits+7)/8) for { drbg.Read(b) - if excess := len(b)*8 - bits; excess != 0 { - b[0] >>= excess - } + excess := len(b)*8 - bits + b[0] &= 0b1111_1111 >> excess result, err := millerRabinIteration(mr, b) if err != nil { // b was rejected. @@ -238,6 +296,8 @@ func isPrime(w []byte) bool { // // Higher values cause fewer Miller-Rabin tests of composites (nothing can help // with the final test on the actual prime) but make InverseVarTime take longer. +// There are diminishing returns: including the 75th prime would increase the +// success rate of trial division by 0.05%. var productOfPrimes = []byte{ 0x10, 0x6a, 0xa9, 0xfb, 0x76, 0x46, 0xfa, 0x6e, 0xb0, 0x81, 0x3c, 0x28, 0xc5, 0xd5, 0xf0, 0x9f, 0x07, 0x7e, 0xc3, 0xba, 0x23, 0x8b, 0xfb, 0x99, 0xc1, 0xb6, 0x31, 0xa2, 0x03, 0xe8, 0x11, 0x87, diff --git a/src/crypto/internal/fips140/rsa/keygen_test.go b/src/crypto/internal/fips140/rsa/keygen_test.go index 7d613e6ddfd1c8..9104a9dfd85fc8 100644 --- a/src/crypto/internal/fips140/rsa/keygen_test.go +++ b/src/crypto/internal/fips140/rsa/keygen_test.go @@ -6,8 +6,10 @@ package rsa import ( "bufio" + "crypto/internal/fips140/bigmod" "encoding/hex" "fmt" + "math/big" "os" "strings" "testing" @@ -83,8 +85,94 @@ func TestMillerRabin(t *testing.T) { } } +func TestTotient(t *testing.T) { + f, err := os.Open("testdata/gcd_lcm_tests.txt") + if err != nil { + t.Fatal(err) + } + + var GCD, A, B, LCM string + var lineNum int + scanner := bufio.NewScanner(f) + for scanner.Scan() { + lineNum++ + line := scanner.Text() + if len(line) == 0 || line[0] == '#' { + continue + } + + k, v, _ := strings.Cut(line, " = ") + switch k { + case "GCD": + GCD = v + case "A": + A = v + case "B": + B = v + case "LCM": + LCM = v + + t.Run(fmt.Sprintf("line %d", lineNum), func(t *testing.T) { + if A == "0" || B == "0" { + t.Skip("skipping test with zero input") + } + if LCM == "1" { + t.Skip("skipping test with LCM=1") + } + + p, _ := bigmod.NewModulus(addOne(decodeHex(t, A))) + a, _ := bigmod.NewNat().SetBytes(decodeHex(t, A), p) + q, _ := bigmod.NewModulus(addOne(decodeHex(t, B))) + b, _ := bigmod.NewNat().SetBytes(decodeHex(t, B), q) + + gcd, err := bigmod.NewNat().GCDVarTime(a, b) + // GCD doesn't work if a and b are both even, but LCM handles it. + if err == nil { + if got := strings.TrimLeft(hex.EncodeToString(gcd.Bytes(p)), "0"); got != GCD { + t.Fatalf("unexpected GCD: got %s, want %s", got, GCD) + } + } + + lcm, err := totient(p, q) + if oddDivisorLargerThan32Bits(decodeHex(t, GCD)) { + if err != errDivisorTooLarge { + t.Fatalf("expected divisor too large error, got %v", err) + } + t.Skip("GCD too large") + } + if err != nil { + t.Fatalf("failed to calculate totient: %v", err) + } + if got := strings.TrimLeft(hex.EncodeToString(lcm.Nat().Bytes(lcm)), "0"); got != LCM { + t.Fatalf("unexpected LCM: got %s, want %s", got, LCM) + } + }) + default: + t.Fatalf("unknown key %q on line %d", k, lineNum) + } + } + if err := scanner.Err(); err != nil { + t.Fatal(err) + } +} + +func oddDivisorLargerThan32Bits(b []byte) bool { + x := new(big.Int).SetBytes(b) + x.Rsh(x, x.TrailingZeroBits()) + return x.BitLen() > 32 +} + +func addOne(b []byte) []byte { + x := new(big.Int).SetBytes(b) + x.Add(x, big.NewInt(1)) + return x.Bytes() +} + func decodeHex(t *testing.T, s string) []byte { t.Helper() + if len(s)%2 != 0 { + s = "0" + s + } b, err := hex.DecodeString(s) if err != nil { t.Fatalf("failed to decode hex %q: %v", s, err) diff --git a/src/crypto/internal/fips140/rsa/pkcs1v22.go b/src/crypto/internal/fips140/rsa/pkcs1v22.go index a62d7e485f6df7..a5bc56dafcd9ff 100644 --- a/src/crypto/internal/fips140/rsa/pkcs1v22.go +++ b/src/crypto/internal/fips140/rsa/pkcs1v22.go @@ -264,8 +264,6 @@ func PSSMaxSaltLength(pub *PublicKey, hash fips140.Hash) (int, error) { } // SignPSS calculates the signature of hashed using RSASSA-PSS. -// -// In FIPS mode, rand is ignored and can be nil. func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte, saltLength int) ([]byte, error) { fipsSelfTest() fips140.RecordApproved() @@ -286,12 +284,8 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash fips140.Hash, hashed []byte, fips140.RecordNonApproved() } salt := make([]byte, saltLength) - if fips140.Enabled { - drbg.Read(salt) - } else { - if _, err := io.ReadFull(rand, salt); err != nil { - return nil, err - } + if err := drbg.ReadWithReaderDeterministic(rand, salt); err != nil { + return nil, err } emBits := priv.pub.N.BitLen() - 1 @@ -374,8 +368,6 @@ func checkApprovedHash(hash fips140.Hash) { } // EncryptOAEP encrypts the given message with RSAES-OAEP. -// -// In FIPS mode, random is ignored and can be nil. func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) { // Note that while we don't commit to deterministic execution with respect // to the random stream, we also don't apply MaybeReadByte, so per Hyrum's @@ -408,13 +400,8 @@ func EncryptOAEP(hash, mgfHash fips140.Hash, random io.Reader, pub *PublicKey, m db[len(db)-len(msg)-1] = 1 copy(db[len(db)-len(msg):], msg) - if fips140.Enabled { - drbg.Read(seed) - } else { - _, err := io.ReadFull(random, seed) - if err != nil { - return nil, err - } + if err := drbg.ReadWithReaderDeterministic(random, seed); err != nil { + return nil, err } mgf1XOR(db, mgfHash, seed) diff --git a/src/crypto/internal/fips140/rsa/rsa.go b/src/crypto/internal/fips140/rsa/rsa.go index 7f759cff640e35..0bbf7010506107 100644 --- a/src/crypto/internal/fips140/rsa/rsa.go +++ b/src/crypto/internal/fips140/rsa/rsa.go @@ -229,7 +229,7 @@ func checkPrivateKey(priv *PrivateKey) error { // Check that de ≡ 1 mod p-1, and de ≡ 1 mod q-1. // // This implies that e is coprime to each p-1 as e has a multiplicative - // inverse. Therefore e is coprime to lcm(p-1,q-1,r-1,...) = exponent(ℤ/nℤ). + // inverse. Therefore e is coprime to lcm(p-1,q-1) = λ(N). // It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1 mod p. Thus a^de ≡ a // mod n for all a coprime to n, as required. // diff --git a/src/crypto/internal/fips140/rsa/testdata/gcd_lcm_tests.txt b/src/crypto/internal/fips140/rsa/testdata/gcd_lcm_tests.txt new file mode 100644 index 00000000000000..b5a0c17ed2a204 --- /dev/null +++ b/src/crypto/internal/fips140/rsa/testdata/gcd_lcm_tests.txt @@ -0,0 +1,279 @@ +# GCD tests. +# +# These test vectors satisfy gcd(A, B) = GCD and lcm(A, B) = LCM. + +GCD = 0 +A = 0 +B = 0 +# Just to appease the syntax-checker. +LCM = 0 + +GCD = 1 +A = 92ff140ac8a659b31dd904161f9213706a08a817ae845e522c3af0c9096699e059b47c8c2f16434b1c5766ebb384b79190f2b2a62c2378f45e116890e7bb407a +B = 2f532c9e5902b0d68cd2ed69b2083bc226e8b04c549212c425a5287bb171c6a47fcb926c70cc0d34b8d6201c617aee66af865d31fdc8a2eeb986c19da8bb0897 +LCM = 1b2c97003e520b0bdd59d8c35a180b4aa36bce14211590435b990ad8f4c034ce3c77899581cb4ee1a022874203459b6d53859ab1d99ff755efa253fc0e5d8487bb000c13c566e8937f0fe90b95b68bc278610d4f232770b08d1f31bee55a03da47f2d0ebb9e7861c4f16cc22168b68593e9efcde00f54104b4c3e1a0b294d7f6 + +GCD = a +A = faaffa431343074f5c5d6f5788500d7bc68b86eb37edf166f699b4d75b76dae2cb7c8f6eccae8f18f6d510ef72f0b9633d5740c0bebb934d3be796bd9a53808e +B = 2f48ec5aa5511283c2935b15725d30f62244185573203b48c7eb135b2e6db5c115c9446ac78b020574665b06a75eb287e0dbeb5da7c193294699b4c2129d2ac4 +LCM = 4a15f305e9622aa19bd8f39e968bfc16d527a47f7a5219d7b02c242c77ef8b608a4a6141f643ca97cedf07c0f1f3e8879d2568b056718aa15c0756899a08ccbe0a658bae67face96fa110edb91757bfa4828e8ff7c5d71b204f36238b12dd26f17be8ba9771f7068d63e41d423671f898f054b1187605754bc5546f2b02c5ac + +GCD = 16 +A = cf0b21bde98b41b479ac8071086687a6707e9efaacd4e5299668ce1be8b13290f27fd32ae68df87c292e8583a09d73ec8e8a04a65a487380dcd7dacca3b6e692 +B = 3be3f563f81d5ad5c1211db7eff430aa345e830ce07b4bde7d4d32dba3ac618d2034351e5435fd6c7f077971fb4a1e83a7396a74fdff7fce1267112851db2582 +LCM = 233a2188de2c017235024b182286f17562b2ee5ab9fdfe4efa2f61c4ff99fa44e1ead5bf6cde05bd7502ce78373c83e3f9dbab0c9bb8620a87c2640bce5d12c685af656df789bb3d0ba1edbaa98cf4f0166d422ab17aa6706f8132264d45b72827d6671a00a9186e723379e3a3bb7902d08865f357c74100059f83800241976 + +GCD = 1 +A = dd7b7597d7c1eb399b1cea9b3042c14bd6022d31b1d2642a8f82fc32de6eadaf012fbbf349eaec4922a8468740ca73c6090833d6a69a380ed947b39c2f9b0b76 +B = 8e0dc8654e70eec55496038a8d3fff3c2086bc6dbfc0e2dbdf5bd7de03c5aef01a3982556ac3fc34fd5f13368be6cdc252c82367b7462e210f940f847d382dd9 +LCM = 7ae667df4bd4dd35bbec28719a9f1b5e1f396a9ab386c086742a6ab3014a3386d39f35b50624d0c5b4e6b206c2635c7de5ea69e2faa85dd616a7e36622962a07632839857aa49332942feccff2aee1c962e2f4e8ccfd738a5da5bf528b4c5a2440409350f5a17a39d234403e8482ccf838e0d2758ccfb8018198a51dbb407506 + +GCD = 1 +A = 0 +B = 1 +LCM = 0 + +GCD = 1 +A = 1 +B = 0 +LCM = 0 + +GCD = 1 +A = 1 +B = 1 +LCM = 1 + +GCD = 2b2 +A = dfccaa3549c1b59ab3e114fe87dc5d187719abad58c51724e972741eb895ab79a49f385f61d531ec5c88dbb505ae375093fa848165f71a5ed65e7832a42ade191a +B = fa58a81f43088da45e659fc1117d0f1cd015aa096c8e5377cf1832191baf7cc28b5c24998b93b64f8900a0973faedb9babaaf1854345f011739da8f1175d9684c +LCM = 5132f7ab7a982b9dc55114bd96800b7637f9742cf8a7a00a0d69d5e4574fc85792c89a1c52bcfc74b9d7f3f6164819466c46b2d622e280ced7ad1211604084a15dc1fd1951a05c8ce37122c0ec15891d818a70d3763670ea3195098de9b1ca50ea89893a9753fb9ea801541058f44801f7f50967124abfc864a2b01c41f94193c + +GCD = 8e +A = 248d96a8a4cab0a1b194e08c1146868b094597cadbc35531f0ed2d77cba9f15cb5cc7c10e64ce054bf93396d25259d750b3de3aba65073db1fd2b852a6454ac1a +B = 4c7bad8e1844901fd6a2ce2edc82e698d28ec95d6672ca148d85b49ecc78dd0a8b870e202244210bc98592b99ff6abbd20630f9eee7d46b15ccfae8d08b86799de +LCM = 13b01f9d9c6c13e90c97e3d95bbce5a835c631b3de3bd4ff5df13ad850f5223dbdf71c53912275d0397df9335ef3a3ba8e4684c6b25962bb7b18bc74144cb5edf0196f79863a7ff032619a71646a92281f7baace7f223d254cb4d05ec19bf8d4c8ce4455a9d770daec89c0d3cf338cbdae39cf982b3c4568f5c9def4e1133d28a + +GCD = 3e55 +A = 2fa97382f46676b7a4cc2b8153f17b58792d24660e187d33ce55c81cc193ccb6e1e2b89feea1d5fd8faa36e13bf947fb48635e450a4d1488d0978324194a1f43c6 +B = ab08ad074139963bc18e5d87ba68db64ca6f4c279616c64039b02c55f2375b3bc04114e8e05e1ba92fb6470768f61d123845aea36774c18612736a220934561faf +LCM = 82c7c377ecda2cb9228604cd287df5eff94edd4a539c3eb3b3fdd4b4a79d2f4eaf2b22f8286272d3dad2e370cfcd9ea4d93ebb3f049c52b8fa23b68a5bf79af989822e2cfb978f68c6a5058f47319dffcb455b089b06ae6db9e5c8a2b6e951d6e118bd2b4cd08b6e5733476a446a57387d940d1289ec00e24315821ed3a5daf2 + +GCD = a7a +A = 923706dfed67834a1e7e6c8e8e9f93bfbc0b43ca1f324886cf1f1380fb9b77109275d4b50af1b7689802fe9b3623ac46c7ba0e17e908c20278127b07a5c12d86ec +B = 64473e878a29021fac1c1ce34a63eae1f4f83ee6851333b67213278b9a4a16f005cba0e8cdb410035bb580062f0e486c1a3a01f4a4edf782495f1dc3ebfa837d86 +LCM = 57785ca45b8873032f1709331436995525eed815c55140582ce57fd852116835deac7ca9d95ce9f280e246ea4d4f1b7140ab7e0dd6dc869de87f1b27372098b155ad0a1828fd387dff514acc92eae708609285edaab900583a786caf95153f71e6e6092c8c5ee727346567e6f58d60a5e01c2fa8ebcf86da9ea46876ecc58e914 + +GCD = 42 +A = 0 +B = 42 +LCM = 0 + +GCD = 42 +A = 42 +B = 0 +LCM = 0 + +GCD = 42 +A = 42 +B = 42 +LCM = 42 + +GCD = f60d +A = ef7886c3391407529d5cf2e75ed53e5c3f74439ad2e2dc48a79bc1a5322789b4ced2914b97f8ff4b9910d212243b54001eb8b375365b9a87bd022dd3772c78a9fd63 +B = d1d3ec32fa3103911830d4ec9f629c5f75af7039e307e05bc2977d01446cd2cbeeb8a8435b2170cf4d9197d83948c7b8999d901fe47d3ce7e4d30dc1b2de8af0c6e4 +LCM = cc376ed2dc362c38a45a719b2ed48201dab3e5506e3f1314e57af229dc7f3a6a0dad3d21cfb148c23a0bbb0092d667051aa0b35cff5b5cc61a7c52dec4ed72f6783edf181b3bf0500b79f87bb95abc66e4055f259791e4e5eb897d82de0e128ecf8a091119475351d65b7f320272db190898a02d33f45f03e27c36cb1c45208037dc + +GCD = 9370 +A = 1ee02fb1c02100d1937f9749f628c65384ff822e638fdb0f42e27b10ee36e380564d6e861fcad0518f4da0f8636c1b9f5124c0bc2beb3ca891004a14cd7b118ddfe0 +B = 67432fd1482d19c4a1c2a4997eab5dbf9c5421977d1de60b739af94c41a5ad384cd339ebfaa43e5ad6441d5b9aaed5a9f7485025f4b4d5014e1e406d5bd838a44e50 +LCM = 159ff177bdb0ffbd09e2aa7d86de266c5de910c12a48cbe61f6fa446f63a2151194777555cd59903d24cb30965973571fb1f89c26f2b760526f73ded7ee8a34ebcecd1a3374a7559bcdb9ac6e78be17a62b830d6bb3982afdf10cf83d61fd0d588eab17d6abef8e6a7a5763fcb766d9a4d86adf5bb904f2dd6b528b9faec603987a0 + +GCD = c5f +A = 5a3a2088b5c759420ed0fb9c4c7685da3725b659c132a710ef01e79435e63d009d2931ea0a9ed9432f3d6b8851730c323efb9db686486614332c6e6ba54d597cf98 +B = 1b1eb33b006a98178bb35bbcf09c5bebd92d9ace79fa34c1567efa8d6cf6361547807cd3f8e7b8cd3ddb6209dccbae4b4c16c8c1ec19741a3a57f61571882b7aed7 +LCM = c5cbbbe9532d30d2a7dd7c1c8a6e69fd4fa4828a844d6afb44f3747fef584f7f1f3b835b006f8747d84f7699e88f6267b634e7aef78d6c7584829537d79514eec7d11219721f91015f5cefdc296261d85dba388729438991a8027de4827cd9eb575622e2912b28c9ce26d441e97880d18db025812cef5de01adeaec1322a9c9858 + +GCD = e052 +A = 67429f79b2ec3847cfc7e662880ab1d94acdf04284260fcfffd67c2862d59704ed45bcc53700c88a5eea023bc09029e9fd114fc94c227fd47a1faa1a5ef117b09bd2 +B = 39faa7cbdeb78f9028c1d50ab34fbe6924c83a1262596f6b85865d4e19cc258b3c3af1ee2898e39e5bee5839e92eac6753bbbb0253bd576d1839a59748b778846a86 +LCM = 1ab071fb733ef142e94def10b26d69982128561669e58b20b80d39cf7c2759d26b4a65d73b7f940c6e8fc417180ef62d7e52ac24678137bd927cd8d004ad52b02affe176a1ecde903dbc26dcc705678f76dd8cd874c0c3fe737474309767507bbe70dd7fb671bbb3694cedf0dcdaa0c716250ddd6dfec525261572fa3e1387f7b906 + +GCD = 3523 +A = 0 +B = 3523 +LCM = 0 + +GCD = 3523 +A = 3523 +B = 0 +LCM = 0 + +GCD = 3523 +A = 3523 +B = 3523 +LCM = 3523 + +GCD = f035a941 +A = 16cd5745464dfc426726359312398f3c4486ed8aaeea6386a67598b10f744f336c89cdafcb18e643d55c3a62f4ab2c658a0d19ea3967ea1af3aee22e11f12c6df6e886f7 +B = 74df09f309541d26b4b39e0c01152b8ad05ad2dfe9dd2b6706240e9d9f0c530bfb9e4b1cad3d4a94342aab309e66dd42d9df01b47a45173b507e41826f24eb1e8bcc4459 +LCM = b181771d0e9d6b36fdfcbf01d349c7de6b7e305e1485ea2aa32938aa919a3eee9811e1c3c649068a7572f5d251b424308da31400d81ac4078463f9f71d7efd2e681f92b13a6ab3ca5c9063032dcbdf3d3a9940ce65e54786463bbc06544e1280f25bc7579d264f6f1590cf09d1badbf542ce435a14ab04d25d88ddbac7d22e8cae1c91f + +GCD = 33ad1b8f +A = 1af010429a74e1b612c2fc4d7127436f2a5dafda99015ad15385783bd3af8d81798a57d85038bcf09a2a9e99df713b4d6fc1e3926910fbbf1f006133cb27dc5ebb9cca85 +B = 92a4f45a90965a4ef454f1cdd883d20f0f3be34d43588b5914677c39d577a052d1b25a522be1a656860a540970f99cbc8a3adf3e2139770f664b4b7b9379e13daf7d26c +LCM = 4c715520ed920718c3b2f62821bc75e3ff9fd184f76c60faf2906ef68d28cd540d3d6c071fa8704edd519709c3b09dfaee12cb02ab01ad0f3af4f5923d5705ce6d18bcab705a97e21896bb5dd8acb36ee8ec98c254a4ddc744297827a33c241f09016a5f109248c83dd41e4cea73ce3eabb28d76678b7e15545b96d22da83c111b6b624 + +GCD = dc0429aa +A = ccb423cfb78d7150201a97114b6644e8e0bbbb33cadb0ef5da5d3c521a244ec96e6d1538c64c10c85b2089bdd702d74c505adce9235aa4195068c9077217c0d431de7f96 +B = 710786f3d9022fc3acbf47ac901f62debcfda684a39234644bac630ab2d211111df71c0844b02c969fc5b4c5a15b785c96efd1e403514235dc9356f7faf75a0888de5e5a +LCM = 6929af911850c55450e2f2c4c9a72adf284fe271cf26e41c66e1a2ee19e30d928ae824f13d4e2a6d7bb12d10411573e04011725d3b6089c28d87738749107d990162b485805f5eedc8f788345bcbb5963641f73c303b2d92f80529902d3c2d7899623958499c8a9133aae49a616c96a2c5482a37947f23af18c3247203ac2d0e760340e6 + +GCD = 743166058 +A = 16cd476e8031d4624716238a3f85badd97f274cdfd9d53e0bd74de2a6c46d1827cc83057f3889588b6b7ca0640e7d743ed4a6eaf6f9b8df130011ecc72f56ef0af79680 +B = 86eba1fc8d761f22e0f596a03fcb6fe53ad15a03f5b4e37999f60b20966f78ba3280f02d3853f9ace40438ccfaf8faed7ace2f2bf089b2cdd4713f3f293bf602666c39f8 +LCM = 1a7a1b38727324d6ba0290f259b8e2b89c339b2445cada38a5a00ded1468ab069f40678ce76f7f78c7c6f97783cc8a49ef7e2a0c73abbac3abc66d1ce99566ce7f874a8949ca3442051e71967695dc65361184748c1908e1b587dc02ed899a524b34eb30b6f8db302432cfa1a8fbf2c46591e0ab3db7fd32c01b1f86c39832ee9f0c80 + +GCD = 6612ba2c +A = 0 +B = 6612ba2c +LCM = 0 + +GCD = 6612ba2c +A = 6612ba2c +B = 0 +LCM = 0 + +GCD = 6612ba2c +A = 6612ba2c +B = 6612ba2c +LCM = 6612ba2c + +GCD = 2272525aa08ccb20 +A = 11b9e23001e7446f6483fc9977140d91c3d82568dabb1f043a5620544fc3dda233b51009274cdb004fdff3f5c4267d34181d543d913553b6bdb11ce2a9392365fec8f9a3797e1200 +B = 11295529342bfb795f0611d03afb873c70bd16322b2cf9483f357f723b5b19f796a6206cf3ae3982daaeafcd9a68f0ce3355a7eba3fe4e743683709a2dd4b2ff46158bd99ff4d5a0 +LCM = 8d4cbf00d02f6adbaa70484bcd42ea932000843dcb667c69b75142426255f79b6c3b6bf22572597100c06c3277e40bf60c14c1f4a6822d86167812038cf1eefec2b0b19981ad99ad3125ff4a455a4a8344cbc609e1b3a173533db432bd717c72be25e05ed488d3970e7ed17a46353c5e0d91c8428d2fec7a93210759589df042cab028f545e3a00 + +GCD = 3480bf145713d56f9 +A = 8cf8ef1d4f216c6bcec673208fd93b7561b0eb8303af57113edc5c6ff4e1eeae9ddc3112b943d947653ba2179b7f63505465126d88ad0a0a15b682f5c89aa4a2a51c768cd9fdeaa9 +B = a6fd114023e7d79017c552a9051ca827f3ffa9f31e2ee9d78f8408967064fcdc9466e95cc8fac9a4fa88248987caf7cf57af58400d27abd60d9b79d2fe03fad76b879eceb504d7f +LCM = 1c05eee73a4f0db210a9007f94a5af88c1cdd2cba456061fd41de1e746d836fa4e0e972812842e0f44f10a61505f5d55760c48ba0d06af78bb6bde7da8b0080b29f82b1161e9c0b5458e05ac090b00f4d78b1cc10cf065124ba610e3acab092a36fe408525e21c0ddc7c9696ed4e48bd2f70423deecfe62cecc865c6088f265da0e5961d3f3a84f + +GCD = 917e74ae941fcaae +A = 652f8a92d96cbf0a309629011d0fbaceb1266bc2e8243d9e494eead4cf7100c661b537a8bea93dec88cfc68597d88a976c125c3b4de19aba38d4ea9578202e59848d42652518348a +B = 32e07b71979d57e8344e97c39680a61e07d692d824ae26b682156890792d8a766ee29a4968f461aaced5bf049044fba2f4120b1c1f05985676f975d4582e9e82750d73c532cd07b2 +LCM = 23620c7b897dc26c7717e32f3517ac70bf09fbe08f7255ab010cf4cf946f4e96304c425043452c5d5a0e841d3a3cfd9c2d84d9256f3b5974fe3ebfa9255fe20a710d3e6511606c0d85970381101c7f4986d65ad6a73a71507f146b11f903043cfa805cc0b14d4f3072da98bf22282f7762040406c02d5b3ef9e7587f63bab8b29c61d8e30911aa96 + +GCD = 2b9adc82005b2697 +A = 19764a84f46045ef1bca571d3cbf49b4545998e64d2e564cc343a53bc7a0bcfbe0baa5383f2b346e224eb9ce1137d9a4f79e8e19f946a493ff08c9b423574d56cbe053155177c37 +B = 1bbd489ad2ab825885cdac571a95ab4924e7446ce06c0f77cf29666a1e20ed5d9bc65e4102e11131d824acad1592075e13024e11f12f8210d86ab52aa60deb250b3930aabd960e5a +LCM = 1032a0c5fffc0425e6478185db0e5985c645dd929c7ebfeb5c1ee12ee3d7b842cfab8c9aa7ff3131ac41d4988fb928c0073103cea6bb2cc39808f1b0ad79a6d080eac5a0fc6e3853d43f903729549e03dba0a4405500e0096b9c8e00510c1852982baec441ed94efb80a78ed28ed526d055ad34751b831b8749b7c19728bf229357cc5e17eb8e1a + +GCD = 8d9d4f30773c4edf +A = 0 +B = 8d9d4f30773c4edf +LCM = 0 + +GCD = 8d9d4f30773c4edf +A = 8d9d4f30773c4edf +B = 0 +LCM = 0 + +GCD = 8d9d4f30773c4edf +A = 8d9d4f30773c4edf +B = 8d9d4f30773c4edf +LCM = 8d9d4f30773c4edf + +GCD = 6ebd8eafb9a957a6c3d3d5016be604f9624b0debf04d19cdabccf3612bbd59e00 +A = 34dc66a0ffd5b8b5e0ffc858dfc4655753e59247c4f82a4d2543b1f7bb7be0e24d2bbf27bb0b2b7e56ee22b29bbde7baf0d7bfb96331e27ba029de9ffdff7bdb7dc4da836d0e58a0829367ec84ea256833fd4fe1456ad4dd920557a345e12000 +B = 1f3406a20e20ebf96ccb765f898889a19b7636608fd7dc7c212607b641399543f71111d60e42989de01eaa6ff19a86ea8fbde1a3d368c0d86dc899e8e250fc764090f337958ca493119cbb4ad70cbfae7097d06d4f90ec62fbdd3f0a4496e600 +LCM = ee502c50e3667946e9089d0a9a0382e7fd0b75a17db23b56a0eec997a112c4dbd56d188808f76fe90451e5605550c9559ef14a95014c6eb97e9c1c659b98515c41470142843de60f72fb4c235faa55b0a97d943221003d44e2c28928f0b84bf071256254897ed31a7fd8d174fc962bc1311f67900ac3abcad83a28e259812f1ee229511ab1d82d41f5add34693ba7519babd52eb4ec9de31581f5f2e40a000 + +GCD = ef7399b217fc6a62b90461e58a44b22e5280d480b148ec4e3b4d106583f8e428 +A = 7025e2fe5f00aec73d90f5ad80d99ca873f71997d58e59937423a5e6ddeb5e1925ed2fd2c36a5a9fc560c9023d6332c5d8a4b333d3315ed419d60b2f98ccf28bbf5bf539284fd070d2690aeaac747a3d6384ee6450903a64c3017de33c969c98 +B = df0ac41dbabce1deeb0bceb1b65b1079850052ecf6534d0cff84a5a7fb5e63baee028d240f4419925154b96eaa69e8fbb1aae5102db7916234f290aa60c5d7e69406f02aeea9fe9384afbff7d878c9ac87cd31f7c35dff243b1441e09baff478 +LCM = 687669343f5208a6b2bb2e2efcac41ec467a438fde288cc5ef7157d130139ba65db9eb53e86a30c870bd769c0e0ab15a50f656cd9626621ae68d85eaff491b98da3ea5812062e4145af11ea5e1da457084911961ef2cd2ac45715f885ba94b4082aa76ffd1f32461f47c845b229d350bf36514c5ce3a7c782418746be342eca2721346ade73a59475f178c4f2448e1326110f5d26a0fef1a7a0c9288489e4dc8 + +GCD = 84b917557acf24dff70cb282a07fc52548b6fbbe96ca8c46d0397c8e44d30573 +A = 81dbb771713342b33912b03f08649fb2506874b96125a1ac712bc94bfd09b679db7327a824f0a5837046f58af3a8365c89e06ff4d48784f60086a99816e0065a5f6f0f49066b0ff4c972a6b837b63373ca4bb04dcc21e5effb6dfe38271cb0fa +B = 1da91553c0a2217442f1c502a437bb14d8c385aa595db47b23a97b53927b4493dd19f1bc8baf145bc10052394243089a7b88d19b6f106e64a5ab34acad94538ab504d1c8ebf22ac42048bbd1d4b0294a2e12c09fe2a3bd92756ba7578cb34b39 +LCM = 1d0530f8142754d1ee0249b0c3968d0ae7570e37dadbe4824ab966d655abf04cd6de5eb700eba89d8352dec3ae51f2a10267c32fbd39b788c7c5047fe69da3d7ad505435a6212f44899ba7e983bb780f62bcdee6f94b7dba8af7070a4cc008f351ae8be4579bc4a2e5c659ce000ad9c8cdc83723b32c96aeb0f5f4127f6347353d05525f559a8543cd389ad0af6f9d08a75b8c0b32419c097e6efe8746aee92e + +GCD = 66091477ea3b37f115038095814605896e845b20259a772f09405a8818f644aa +A = cedac27069a68edfd49bd5a859173c8e318ba8be65673d9d2ba13c717568754ed9cbc10bb6c32da3b7238cff8c1352d6325668fd21b4e82620c2e75ee0c4b1aff6fb1e9b948bbdb1af83cecdf356299b50543b72f801b6a58444b176e4369e0 +B = 5f64ca1ba481f42c4c9cf1ffa0e515b52aa9d69ceb97c4a2897f2e9fa87f72bae56ee6c5227f354304994c6a5cc742d9f09b2c058521975f69ca5835bce898cf22b28457cd7e28870df14e663bb46c9be8f6662f4ff34d5c4ae17a888eba504e +LCM = c163cb28642e19a40aa77887c63180c2c49fc10cda98f6f929c8131752ea30b5283a814a81681b69b9d1762e6c1a9db85f480bc17f998d235fd7e64c1caa70ef170c9e816d3e80f516b29f2c80cfb68bf208b4d5082ef078da4314b3f20c7d6c54b0aeb378096b029a7b61c0a4cd14aeddc01004c53915a4f692d2291752e5af46b23d7fa6dd61f2d56c6f4bf8e6119688abac8fd7aba80e846a7764bb3fca0 + +GCD = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 +A = 0 +B = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 +LCM = 0 + +GCD = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 +A = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 +B = 0 +LCM = 0 + +GCD = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 +A = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 +B = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 +LCM = bb80bf51757ba696c700fa4e4c0132b3151d2bf9ebff8382f808ded78be67182 + +GCD = 120451d8307219aa0c96f328ad653ccd462e92423ca93ed8a3dde45bf5cb9b13cdaf9800e4d05dd71c4db6a129fb3280ee4ec96ec5297d881c1a8b5efccbd91fef21f5c5bf5fba42a4c8eaa358f620a074b7a17054527bdaa58d5acaa0dfdc48ecba1a10ebf4d57bb4215de406e6be13fed3fe493b1cd1e2d11a8d4ac03c47756 +A = 3f8179a8e1f0b342475a855c3e1bae402dd41424cf24a0b4d2e263c8efb08bde7d92eae8607fb5e88b1378f0f1bd0733f229a35be6b1383a48d32749d5d6b32427d26323b7ab05bb5781289e96bfbc21971439319b15f6c0fe93fdb35d0b67ec41443c59a081dd3cef047ac797fccb45bece84c0bb0bb7e1797259526d8ec9cc63ba4d32cfc692ccd3d243cb2b53ac216312f3a8e8c0daa09d21b6150d697639a5e52059414a417c607be8ec0eee2e708219cadbaf37a369c4485b01ed87bbc2 +B = 2c474e396a2dd9cd10b9d7313f69d3b4ca123e9fd853edd488339236d14c56453a1381958864a04d2624e81995dabcdd0ccf60db9917813f887de68da075d0ea4440001e18f470e43b38ee3440b49be651d709fbdef980e3e4149913f4ae2681124f54523f4881376ddb533b5219e804cc26f4c2e577be4e02613c4da80ba1215775b0a5178a965ad47bd2befb32493943ded1004ef66347b4983f8d1ba990d4a943505dfce6debcfb322842ed88106cd6dee9aa592ff0d2274bc727a6e1f14c +LCM = 9c129cf649555bfd2d3d9c64dc6d6f022295e53bca5d2f218adaa66aa60eb4694429b7e83bf81b6df4459c5104023ab9a33f006ffcd8114507baa17e2ef6fe23ebdd4740f66879033da2041f2cb7ba517ad3526ffe75614ea9432c085f71b2d65a736bac7ba42b639e330b82733372083843dcb78b6a273ab20e0d4b7c8998a14048aa15bb20a0a0bd997917107274c89b4cec175fb98043d52e6c555bd9e0036566d052a6d4e7e276d1e8835e1f06e3ca46d47747ba586e95fb1a790d992834b7c3e136141eb8a434e6c12067246ac3c0a81c69e03b1ed28aa0b3173d6eff83d278c2f461a47a416f3f9a5dae3bb410fd18817bd4115e7f1e84b936cc02364 + +GCD = 95aa569a2c76854300d7660847dd20fe0b8c445fdbcaa98465cee61aee76ad6a438e75a8c573198570ffb62bc07ec3a2be0ae0a1f631670fa88d6f75f3161e8b9a4d44b6801ffc884c7f469c5ed1f27b1edecce9f2977f9e92d1a3b230492fea7e6f2af739dc158a7fbd29856cbedb57b4119e64b27ab09eb1c2df01507d6e7fd +A = 4c653b5bfec44e9be100c064dffe5d8cd59b0cf4cc56b03eabb4ef87cfda6506c9a756b811907fe9d8b783eb7a0b9e129773bf1da365ddb488d27b16fb983e89345d1ccdb4f06a67a11925c3f266373be5d7b0075189c6f3c2157e2da197058fe0a7bcc50adc34e99e254a29abbe2d5948d3157e1b0c3fca3d641760f7b9862843b63abef0b3d83fd486f4526b30382fda355575da30e9a106718a3921774c4d69f5311f8d737fe618f5236b4763fe1b2ee7f13184db67367d3903c535ff6d7b +B = 2dcca83c99a28e9fd2f84e78973699baf2f04fd454094730948b22477834a0064817b86e0835e6d7b26e5b0b1dcf4ad91a07ac0780d6522df1fcac758cf5db6c2a5623d7c0f1afefd5718f7b6de639867d07a9ec525991304e9355d1635104bea837f74758d6aa2aab4e4afbb606af1d98de7417505e4710cd0589bdff9a0bf38a857cc59a5f1781043e694fc2337fd84bdeb28b13a222bb09328a81ec409ad586e74236393d27398cc24d412135e34247c589149e134b97f4bd538ac9a3424b +LCM = 1760c0b0066aa0695767099e87e9388729ea89b8e8c36bddcd04d257591e741613c07b0e69447c0a468c33a745084171e06523d987d8db40a1433bf435325e8a724a0876503b34495170ff3671d42117a2e4f3a75b1d9dd809a34fa0fb26fe50d84f80a9b02e40190e5efb927a5a61a03f13edbce2e666af6c3a2a9bcb84e47e3090008753ff27c4b8cf06480f471379a93f5230923623a83b286b71a555cd5e5347282f664ed90b14b2c4de84a70375e488211a7b3931119ef3bbe029b712389fe784818a0bf29d80733ce9cc940c547aa1eb3f06d492eb676bf37802283c82ce76156dfaab5c2d5107e08062681b5fa169f6eb68e1ab8bd9b2005e90bd4fd + +GCD = 244b9b1290cf5b4ba2f810574c050651489f2d3a2b03e702b76ebfaf4e33de9bbe5da24c919e68d3a72eadd35982b3a89c6b18b38ff7082ac65263e52b6ec75a5717b971c98257b194c828bff0216a99536603b41a396ea2fb50f5ea7cf3edf10bb0d039123e78593ae9ffcbbba02e51e038533e83b6bc73c70551d6467f39809 +A = 41a0b1310669500681cdf888836f6c556758750f562d743ac780dd4c0d161856380e44fdbb1f8a2786bf45be6b0e7f1cb2cd85f6b9e50acc72793d92383c7d7fb796fc74d32e8fac8225bdc19ae47546d9c9c75f5f06ca684f07daccaf89ccf2cddeb7ec255d530c7dd1e71daf44cafdc9d30fbcb1cbaefae3480585f79f4177e3834a5bc91845e2e8cd8aeb27f484e5e5b2c3c076dbb6c23e91303f0a0fdde83cd33a8ea6ed1549e727b4d766c1017c169710fd98e1585d60f66e121f9180b3 +B = 251f5aeaa60b3959285f49540cdaf8e21451110bbddb9933bbbcaea3112f4eb45e435a3ba37c52d2ab79ce997a8f6c829b3aa561f2852924b8effb52396d09d2bf257ebb4fb56c7aa25648f69b06d2cd01e876c9f9c0679de9e6fffa79eb7e603723e5af7de46ee405a5a079229577b5b6fffb8d43e391fe6f4eb89638e64d6eff8026249aaa355a91625eb0bfd14caa81e4c3586aaa2e94fde143a44f223a91e226661d12f55dfcdb4215e5a64e14e968005733be6a71c465de312ca109b34a +LCM = 431f918b274f3e43f446e4e85567883d6536a0332db662cef088f5a36b0f4b68372048174ba10fee94b9f8f1c2e189c974be2e6e8ae8e2ae108445326d40f63e38d8d4e2e46174589a3cbc9583e0036dc8146e79eee9e96f4436313b3f143dd0f5aceab05243def7f915169c360f55ef123977cf623c5ba432c3259c62fb5e37d5adab0f24b825aa4ada99ec4e83e9ca4698399e1ed633091ce5f9844c540a642cd264201116ed4168aa2105a5159f5df064f845830c469140f766c7319052ce59bd1ad7c3f2d8c30e54f147f6aeb5586c70c984302ba18d854a60aec01b394c7d66fa33fe18fe4a8cfb3238df219294e6e42190a30d28b10049a1b75853a4e + +GCD = 206695d52bc391a4db61bf8cb6ea96188333a9c78f477ee76976c2346dad682cf56ca6f176d86ef67d41ff5921b6162b0eca52359975872430dd14c45643eacdf028d830770714c033fd150669705851b2f02de932322d271d565d26768530c3f6cb84f0b3356f970b9070b26c050ead0417152c324c8ffe266d4e8b5b7bef3a +A = 1114eb9f1a9d5947eb1399e57f5c980833489685023ed2fe537fe1276c1e026b9a19e6fff55aa889d6c4e977b6e6f3111e2ad463138637b50f42cf32e57d83f282de9e72f813e5969195159a666d74dcd689bd527c60199ae327f7bd548ac36868fea5fdf6f35d19b921e7c10b6448ca480de6826478cd0642d72f05af3f8e65ce42409fbd49f56e81946e89c8e83962c4edc0ed54600600a305e52d081aed3c351e450e11f8fb0ce5754c92cf765b71393b2b7a89c95df79b9ea1b3cb600862 +B = 1d8f3179ca7b5cc7119360c10de939ffa57c9043da2f2b0ca3009c9bdad9f19ed16e3c2c197bef4b527fa1bf2bbab98b77e26c329911db68bd63d3d0fbfc727a977395b9ad067106de3094d68e097830858c5ccfa505fc25e972bdee6f347e7d1163efacd3d29a791ec2a94ffeed467884ae04896efc5e7e5f43d8d76c147e3c9951a1999173bc4e5767d51268b92cc68487ba1295372143b538711e0a62bf0ac111cc750ca4dd6c318c9cbe106d7fc492261404b86a1ba728e2d25b1976dc42 +LCM = f9570211f694141bfb096560551080cbe02a80271b4505591aaea9e3b99ea1d5ac1c1f2378fd72799e117ac2a73381b1ad26314e39972164d93971479ee3ba21a4d98cef0bd299d540ce5826995dcee0de420dff73d30b23cbf3188c625c7696df517535bc5675d71faa00807efbebdca547933f4a37849d1c014484a77da6df0670c4974bcc91eb5f5fe5faf9dd095ef195ec32ad9eeebf0e63288b4032ed9e70b888afc642f4ff96f0b4c0a68787301c12e4527fe79bdfe72dd3844ab5e094a9295df6616f24d1b9eeebc2116177dacf91969dda73667bc421ef3ccd8d5c23dddc283f5d36568d31f2654926be67f78e181075bdc148f2b39c630b141ae8a + +GCD = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 +A = 0 +B = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 +LCM = 0 + +GCD = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 +A = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 +B = 0 +LCM = 0 + +GCD = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 +A = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 +B = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 +LCM = 3d319c42d872f21131ce5ff3ab8bec94339308e620316dda218e85fedcd511cd62f0b2f3448d5e58fd3520ae8118abd54ead9ad9e8ec3890365c6b2cca2172d4b8839b2d2c5ab02f65180826cb0cd5c9798f5d6261efe6e6ec31dea047da7c486b0590359e6f333557f67ceebf9ea9cd5dd986a999a8c88bdbd0ca21816b2423 + +GCD = 2 +A = 14e95a85e59ade9ef39e2f400c65db18702fa5fc485b9bba479a5282b2206129160e54f73ef4917983c17b4c5ebff7be112a886de069706eee29ba902515cb038 +B = ddcfff1d39c90c599f55495bf71c1e7597c6b08b7430707f360c6a6e5137bbc7b403c6d9e2c34f3d2f29d5d32b869346853c2de239cc35381bdfb4a01569211a +LCM = 90f38564ee72e55d362c04599e7d74f068c75f541b84e97abba2841f1a9f66b06b5c9009f6a4c2e319fced85270588de03ccebddbd9279aaecb13bdc1dbea7f42acaee751cb7da83779b8785cc86f41b94b13b54964208ca287d981634778d1096f20e76ca636c0717fd27e0800c43f599a5eded807421b502eaf9990a8c8ed8 + +GCD = 4 +A = 3c719c1c363cdeb7b57c2aabb71f425da4c3e6d3e447204d555e7cf0f3d372bdda906f36078045044978dafc20171767c8b1464d52dfdf3e2ba8a4906da033a8 +B = 30fe0ef151ac51404e128c064d836b191921769dc02d9b09889ed40eb68d15bfdd2edea33580a1a4d7dcee918fefd5c776cbe80ca6131aa080d3989b5e77e1b24 +LCM = 2e4526157bbd765b0486d90bcd4728f890bc6dbd9a855c67ca5cb2d6b48f8e74e1d99485999e04b193afca58dbf282610185d6c0272007744ff26e00dbdc813929b47940b137dc56ba974da07d54a1c50ec4a5c2b26e83f47cf17f4ccce8c3687e8d1e91d7c491a599f3d057c73473723ce9eee52c20fe8ae1595447552a7ee8 + +GCD = 10 +A = 44e04071d09119ea9783a53df35de4a989200133bb20280fdca6003d3ca63fdd9350ad1a1673d444d2f7c7be639824681643ec4f77535c626bd3ee8fa100e0bb0 +B = ca927a5a3124ce89accd6ac41a8441d352a5d42feb7f62687a5ebc0e181cc2679888ecc2d38516bdc3b3443550efccac81e53044ae9341ecace2598fe5ce67780 +LCM = 36805ba9b2412a0cb3fe4ed9bdabfa55515c9d615a3d0af268c45c5f6098d2de4a583f3791f1e3883c55d51ce23c5658fd0e8faa9a3709a1cfbd6a61dbab861690f27c86664f084c86cfd4a183b24aaadf59a6f8cbec04f1b0ded8a59b188cb46ae920052e3e099a570540dbc00f7d4a571eef08aa70d2d189a1804bf04e94a80 + +GCD = 100 +A = 73725032b214a677687c811031555b0c51c1703f10d59b97a4d732b7feaec5726cb3882193419d3f057583b2bc02b297d76bb689977936febaae92638fdfc46a00 +B = 979f4c10f4dc60ad15068cedd62ff0ab293aeaa1d6935763aed41fe3e445de2e366e8661eadf345201529310f4b805c5800b99f351fddab95d7f313e3bb429d900 +LCM = 4460439b4be72f533e9c7232f7e99c48328b457969364c951868ceab56cb2cbbeda8be2e8e3cae45c0758048468b841fdb246b2086d19b59d17b389333166ab82ed785860620d53c44f7aaaff4625ee70fb8072df10fb4d1acb142eadc02978ff2bb07cea9f434e35424b3323a7bda3a1a57aa60c75e49ebb2f59fb653aa77da00 + +GCD = 100000000 +A = f8b4f19e09f5862d79fb2931c4d616a1b8e0dd44781ca52902c8035166c8fca52d33a56ff484c365ec1257de7fa8ed2786163cfc051d5223b4aad859a049e8ba00000000 +B = 6e54cb41b454b080e68a2c3dd0fa79f516eb80239af2be8250ca9cd377ba501aabafc09146fad4402bdc7a49f2c3eec815e25f4c0a223f58e36709eefd92410500000000 +LCM = 6b3020a880ddeff9d17d3dc234da8771962de3322cd15ba7b1e4b1dd4a6a2a802a16c49653865c6fdf6c207cbe0940f8d81ef4cb0e159385fd709d515ee99d109ad9ad680031cbae4eab2ed62944babdade4e3036426b18920022f737897c7d751dce98d626cdda761fec48ad87a377fb70f97a0a15aa3d10d865785719cc5a200000000 diff --git a/src/crypto/internal/fips140/sha3/_asm/go.mod b/src/crypto/internal/fips140/sha3/_asm/go.mod index 265a88d077dd1f..39e83acc943061 100644 --- a/src/crypto/internal/fips140/sha3/_asm/go.mod +++ b/src/crypto/internal/fips140/sha3/_asm/go.mod @@ -2,14 +2,10 @@ module sha3/_asm go 1.22 -require ( - github.com/mmcloughlin/avo v0.6.0 - golang.org/x/crypto v0.25.0 -) +require github.com/mmcloughlin/avo v0.6.0 require ( golang.org/x/mod v0.19.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.22.0 // indirect golang.org/x/tools v0.23.0 // indirect ) diff --git a/src/crypto/internal/fips140/sha3/_asm/go.sum b/src/crypto/internal/fips140/sha3/_asm/go.sum index a2552b8eb9ad32..9e8f35f70fc70d 100644 --- a/src/crypto/internal/fips140/sha3/_asm/go.sum +++ b/src/crypto/internal/fips140/sha3/_asm/go.sum @@ -1,12 +1,8 @@ github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY= github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= diff --git a/src/crypto/internal/fips140/sha3/_asm/keccakf_amd64_asm.go b/src/crypto/internal/fips140/sha3/_asm/keccakf_amd64_asm.go index 02242c9a015d57..5e59b11fc87a3b 100644 --- a/src/crypto/internal/fips140/sha3/_asm/keccakf_amd64_asm.go +++ b/src/crypto/internal/fips140/sha3/_asm/keccakf_amd64_asm.go @@ -13,7 +13,6 @@ import ( . "github.com/mmcloughlin/avo/build" . "github.com/mmcloughlin/avo/operand" . "github.com/mmcloughlin/avo/reg" - _ "golang.org/x/crypto/sha3" ) //go:generate go run . -out ../sha3_amd64.s diff --git a/src/crypto/internal/fips140/subtle/xor_amd64.go b/src/crypto/internal/fips140/subtle/xor_amd64.go deleted file mode 100644 index 3bb2f08b7c909e..00000000000000 --- a/src/crypto/internal/fips140/subtle/xor_amd64.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2018 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. - -//go:build !purego - -package subtle - -//go:noescape -func xorBytes(dst, a, b *byte, n int) diff --git a/src/crypto/internal/fips140/subtle/xor_arm64.go b/src/crypto/internal/fips140/subtle/xor_arm64.go deleted file mode 100644 index 65bab4c6574cde..00000000000000 --- a/src/crypto/internal/fips140/subtle/xor_arm64.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2020 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. - -//go:build !purego - -package subtle - -//go:noescape -func xorBytes(dst, a, b *byte, n int) diff --git a/src/crypto/internal/fips140/subtle/xor_ppc64x.go b/src/crypto/internal/fips140/subtle/xor_asm.go similarity index 73% rename from src/crypto/internal/fips140/subtle/xor_ppc64x.go rename to src/crypto/internal/fips140/subtle/xor_asm.go index 760463c7e5014c..9a5da424aed9f2 100644 --- a/src/crypto/internal/fips140/subtle/xor_ppc64x.go +++ b/src/crypto/internal/fips140/subtle/xor_asm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (ppc64 || ppc64le) && !purego +//go:build (amd64 || arm64 || loong64 || ppc64 || ppc64le || riscv64) && !purego package subtle diff --git a/src/crypto/internal/fips140/subtle/xor_generic.go b/src/crypto/internal/fips140/subtle/xor_generic.go index e575c356960ae9..0b31eec60197d3 100644 --- a/src/crypto/internal/fips140/subtle/xor_generic.go +++ b/src/crypto/internal/fips140/subtle/xor_generic.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!amd64 && !arm64 && !loong64 && !ppc64 && !ppc64le) || purego +//go:build (!amd64 && !arm64 && !loong64 && !ppc64 && !ppc64le && !riscv64) || purego package subtle diff --git a/src/crypto/internal/fips140/subtle/xor_riscv64.s b/src/crypto/internal/fips140/subtle/xor_riscv64.s new file mode 100644 index 00000000000000..b5fa5dcef45e82 --- /dev/null +++ b/src/crypto/internal/fips140/subtle/xor_riscv64.s @@ -0,0 +1,169 @@ +// Copyright 2025 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. + +//go:build !purego + +#include "textflag.h" + +// func xorBytes(dst, a, b *byte, n int) +TEXT ·xorBytes(SB), NOSPLIT|NOFRAME, $0 + MOV dst+0(FP), X10 + MOV a+8(FP), X11 + MOV b+16(FP), X12 + MOV n+24(FP), X13 + + MOV $32, X15 + BLT X13, X15, loop4_check + + // Check alignment - if alignment differs we have to do one byte at a time. + AND $7, X10, X5 + AND $7, X11, X6 + AND $7, X12, X7 + BNE X5, X6, loop4_check + BNE X5, X7, loop4_check + BEQZ X5, loop64_check + + // Check one byte at a time until we reach 8 byte alignment. + MOV $8, X8 + SUB X5, X8 + SUB X8, X13 +align: + MOVBU 0(X11), X16 + MOVBU 0(X12), X17 + XOR X16, X17 + MOVB X17, 0(X10) + ADD $1, X10 + ADD $1, X11 + ADD $1, X12 + SUB $1, X8 + BNEZ X8, align + +loop64_check: + MOV $64, X15 + BLT X13, X15, tail32_check + PCALIGN $16 +loop64: + MOV 0(X11), X16 + MOV 0(X12), X17 + MOV 8(X11), X18 + MOV 8(X12), X19 + XOR X16, X17 + XOR X18, X19 + MOV X17, 0(X10) + MOV X19, 8(X10) + MOV 16(X11), X20 + MOV 16(X12), X21 + MOV 24(X11), X22 + MOV 24(X12), X23 + XOR X20, X21 + XOR X22, X23 + MOV X21, 16(X10) + MOV X23, 24(X10) + MOV 32(X11), X16 + MOV 32(X12), X17 + MOV 40(X11), X18 + MOV 40(X12), X19 + XOR X16, X17 + XOR X18, X19 + MOV X17, 32(X10) + MOV X19, 40(X10) + MOV 48(X11), X20 + MOV 48(X12), X21 + MOV 56(X11), X22 + MOV 56(X12), X23 + XOR X20, X21 + XOR X22, X23 + MOV X21, 48(X10) + MOV X23, 56(X10) + ADD $64, X10 + ADD $64, X11 + ADD $64, X12 + SUB $64, X13 + BGE X13, X15, loop64 + BEQZ X13, done + +tail32_check: + MOV $32, X15 + BLT X13, X15, tail16_check + MOV 0(X11), X16 + MOV 0(X12), X17 + MOV 8(X11), X18 + MOV 8(X12), X19 + XOR X16, X17 + XOR X18, X19 + MOV X17, 0(X10) + MOV X19, 8(X10) + MOV 16(X11), X20 + MOV 16(X12), X21 + MOV 24(X11), X22 + MOV 24(X12), X23 + XOR X20, X21 + XOR X22, X23 + MOV X21, 16(X10) + MOV X23, 24(X10) + ADD $32, X10 + ADD $32, X11 + ADD $32, X12 + SUB $32, X13 + BEQZ X13, done + +tail16_check: + MOV $16, X15 + BLT X13, X15, loop4_check + MOV 0(X11), X16 + MOV 0(X12), X17 + MOV 8(X11), X18 + MOV 8(X12), X19 + XOR X16, X17 + XOR X18, X19 + MOV X17, 0(X10) + MOV X19, 8(X10) + ADD $16, X10 + ADD $16, X11 + ADD $16, X12 + SUB $16, X13 + BEQZ X13, done + +loop4_check: + MOV $4, X15 + BLT X13, X15, loop1 + PCALIGN $16 +loop4: + MOVBU 0(X11), X16 + MOVBU 0(X12), X17 + MOVBU 1(X11), X18 + MOVBU 1(X12), X19 + XOR X16, X17 + XOR X18, X19 + MOVB X17, 0(X10) + MOVB X19, 1(X10) + MOVBU 2(X11), X20 + MOVBU 2(X12), X21 + MOVBU 3(X11), X22 + MOVBU 3(X12), X23 + XOR X20, X21 + XOR X22, X23 + MOVB X21, 2(X10) + MOVB X23, 3(X10) + ADD $4, X10 + ADD $4, X11 + ADD $4, X12 + SUB $4, X13 + BGE X13, X15, loop4 + + PCALIGN $16 +loop1: + BEQZ X13, done + MOVBU 0(X11), X16 + MOVBU 0(X12), X17 + XOR X16, X17 + MOVB X17, 0(X10) + ADD $1, X10 + ADD $1, X11 + ADD $1, X12 + SUB $1, X13 + JMP loop1 + +done: + RET diff --git a/src/crypto/internal/fips140hash/hash.go b/src/crypto/internal/fips140hash/hash.go new file mode 100644 index 00000000000000..6d67ee8b3429a1 --- /dev/null +++ b/src/crypto/internal/fips140hash/hash.go @@ -0,0 +1,34 @@ +// Copyright 2024 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. + +package fips140hash + +import ( + fsha3 "crypto/internal/fips140/sha3" + "crypto/sha3" + "hash" + _ "unsafe" +) + +//go:linkname sha3Unwrap +func sha3Unwrap(*sha3.SHA3) *fsha3.Digest + +// Unwrap returns h, or a crypto/internal/fips140 inner implementation of h. +// +// The return value can be type asserted to one of +// [crypto/internal/fips140/sha256.Digest], +// [crypto/internal/fips140/sha512.Digest], or +// [crypto/internal/fips140/sha3.Digest] if it is a FIPS 140-3 approved hash. +func Unwrap(h hash.Hash) hash.Hash { + if sha3, ok := h.(*sha3.SHA3); ok { + return sha3Unwrap(sha3) + } + return h +} + +// UnwrapNew returns a function that calls newHash and applies [Unwrap] to the +// return value. +func UnwrapNew[Hash hash.Hash](newHash func() Hash) func() hash.Hash { + return func() hash.Hash { return Unwrap(newHash()) } +} diff --git a/src/crypto/internal/fips140only/fips140only.go b/src/crypto/internal/fips140only/fips140only.go index 6ad97befbe06da..147877a34fcb5f 100644 --- a/src/crypto/internal/fips140only/fips140only.go +++ b/src/crypto/internal/fips140only/fips140only.go @@ -5,16 +5,18 @@ package fips140only import ( + "crypto/internal/fips140/drbg" "crypto/internal/fips140/sha256" "crypto/internal/fips140/sha3" "crypto/internal/fips140/sha512" "hash" "internal/godebug" + "io" ) // Enabled reports whether FIPS 140-only mode is enabled, in which non-approved // cryptography returns an error or panics. -var Enabled = godebug.New("#fips140").Value() == "only" +var Enabled = godebug.New("fips140").Value() == "only" func ApprovedHash(h hash.Hash) bool { switch h.(type) { @@ -24,3 +26,8 @@ func ApprovedHash(h hash.Hash) bool { return false } } + +func ApprovedRandomReader(r io.Reader) bool { + _, ok := r.(drbg.DefaultReader) + return ok +} diff --git a/src/crypto/internal/fips140test/acvp_capabilities.json b/src/crypto/internal/fips140test/acvp_capabilities.json index 6502a98db12dcb..b2007438ec1f4f 100644 --- a/src/crypto/internal/fips140test/acvp_capabilities.json +++ b/src/crypto/internal/fips140test/acvp_capabilities.json @@ -11,6 +11,11 @@ {"algorithm":"SHA3-384","messageLength":[{"increment":8,"max":65528,"min":0}],"revision":"2.0"}, {"algorithm":"SHA3-512","messageLength":[{"increment":8,"max":65528,"min":0}],"revision":"2.0"}, + {"algorithm":"SHAKE-128","inBit":false,"outBit":false,"inEmpty":true,"outputLen":[{"min":16,"max":65536,"increment":8}],"revision":"1.0"}, + {"algorithm":"SHAKE-256","inBit":false,"outBit":false,"inEmpty":true,"outputLen":[{"min":16,"max":65536,"increment":8}],"revision":"1.0"}, + {"algorithm":"cSHAKE-128","hexCustomization":false,"outputLen":[{"min":16,"max":65536,"increment":8}],"msgLen":[{"min":0,"max":65536,"increment":8}],"revision":"1.0"}, + {"algorithm":"cSHAKE-256","hexCustomization":false,"outputLen":[{"min":16,"max":65536,"increment":8}],"msgLen":[{"min":0,"max":65536,"increment":8}],"revision":"1.0"}, + {"algorithm":"HMAC-SHA2-224","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":224,"min":32}],"revision":"1.0"}, {"algorithm":"HMAC-SHA2-256","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":256,"min":32}],"revision":"1.0"}, {"algorithm":"HMAC-SHA2-384","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":384,"min":32}],"revision":"1.0"}, @@ -23,5 +28,55 @@ {"algorithm":"HMAC-SHA3-384","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":384,"min":32}],"revision":"1.0"}, {"algorithm":"HMAC-SHA3-512","keyLen":[{"increment":8,"max":524288,"min":8}],"macLen":[{"increment":8,"max":512,"min":32}],"revision":"1.0"}, - {"algorithm":"PBKDF","capabilities":[{"iterationCount":[{"min":1,"max":10000,"increment":1}],"keyLen":[{"min":112,"max":4096,"increment":8}],"passwordLen":[{"min":8,"max":64,"increment":1}],"saltLen":[{"min":128,"max":512,"increment":8}],"hmacAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}],"revision":"1.0"} -] \ No newline at end of file + {"algorithm":"KDA","mode":"HKDF","revision":"Sp800-56Cr1","fixedInfoPattern":"uPartyInfo||vPartyInfo","encoding":["concatenation"],"hmacAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"],"macSaltMethods":["default","random"],"l":2048,"z":[{"min":224,"max":65336,"increment":8}]}, + {"algorithm":"KDA","mode":"OneStepNoCounter","revision":"Sp800-56Cr2","auxFunctions":[{"auxFunctionName":"HMAC-SHA2-224","l":224,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA2-256","l":256,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA2-384","l":384,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA2-512","l":512,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA2-512/224","l":224,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA2-512/256","l":256,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA3-224","l":224,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA3-256","l":256,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA3-384","l":384,"macSaltMethods":["default","random"]},{"auxFunctionName":"HMAC-SHA3-512","l":512,"macSaltMethods":["default","random"]}],"fixedInfoPattern":"uPartyInfo||vPartyInfo","encoding":["concatenation"],"z":[{"min":224,"max":65336,"increment":8}]}, + + {"algorithm":"PBKDF","capabilities":[{"iterationCount":[{"min":1,"max":10000,"increment":1}],"keyLen":[{"min":112,"max":4096,"increment":8}],"passwordLen":[{"min":8,"max":64,"increment":1}],"saltLen":[{"min":128,"max":512,"increment":8}],"hmacAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}],"revision":"1.0"}, + + {"algorithm":"ML-KEM","mode":"keyGen","revision":"FIPS203","parameterSets":["ML-KEM-768","ML-KEM-1024"]}, + {"algorithm":"ML-KEM","mode":"encapDecap","revision":"FIPS203","parameterSets":["ML-KEM-768","ML-KEM-1024"],"functions":["encapsulation","decapsulation"]}, + + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-384","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":384}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":512}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512/224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA2-512/256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-224","derFuncEnabled":false,"entropyInputLen":[192],"nonceLen":[96],"persoStringLen":[192],"additionalInputLen":[0],"returnedBitsLen":224}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-256","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":256}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-384","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":384}]}, + {"algorithm":"hmacDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":false,"capabilities":[{"mode":"SHA3-512","derFuncEnabled":false,"entropyInputLen":[256],"nonceLen":[128],"persoStringLen":[256],"additionalInputLen":[0],"returnedBitsLen":512}]}, + + {"algorithm":"ctrDRBG","revision":"1.0","predResistanceEnabled":[false],"reseedImplemented":true,"capabilities":[{"mode":"AES-256","derFuncEnabled":false,"entropyInputLen":[384],"nonceLen":[0],"persoStringLen":[0],"additionalInputLen":[384],"returnedBitsLen":128}]}, + + {"algorithm":"EDDSA","mode":"keyGen","revision":"1.0","curve":["ED-25519"]}, + {"algorithm":"EDDSA","mode":"keyVer","revision":"1.0","curve":["ED-25519"]}, + {"algorithm":"EDDSA","mode":"sigGen","revision":"1.0","pure":true,"preHash":true,"contextLength":[{"min":0,"max":255,"increment":1}],"curve":["ED-25519"]}, + {"algorithm":"EDDSA","mode":"sigVer","revision":"1.0","pure":true,"preHash":true,"curve":["ED-25519"]}, + + {"algorithm":"ECDSA","mode":"keyGen","revision":"FIPS186-5","curve":["P-224","P-256","P-384","P-521"],"secretGenerationMode":["testing candidates"]}, + {"algorithm":"ECDSA","mode":"keyVer","revision":"FIPS186-5","curve":["P-224","P-256","P-384","P-521"]}, + {"algorithm":"ECDSA","mode":"sigGen","revision":"FIPS186-5","capabilities":[{"curve":["P-224","P-256","P-384","P-521"],"hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}]}, + {"algorithm":"ECDSA","mode":"sigVer","revision":"FIPS186-5","capabilities":[{"curve":["P-224","P-256","P-384","P-521"],"hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}]}, + {"algorithm":"DetECDSA","mode":"sigGen","revision":"FIPS186-5","capabilities":[{"curve":["P-224","P-256","P-384","P-521"],"hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}]}, + + {"algorithm":"ACVP-AES-CBC","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"revision":"1.0"}, + {"algorithm":"ACVP-AES-CTR","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"payloadLen":[{"min":8,"max":128,"increment":8}],"incrementalCounter":true,"overflowCounter":true,"performCounterTests":true,"revision":"1.0"}, + {"algorithm":"ACVP-AES-GCM","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"payloadLen":[{"min":0,"max":65536,"increment":8}],"aadLen":[{"min":0,"max":65536,"increment":8}],"tagLen":[96,104,112,120,128],"ivLen":[96],"ivGen":"external","revision":"1.0"}, + {"algorithm":"ACVP-AES-GCM","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"payloadLen":[{"min":0,"max":65536,"increment":8}],"aadLen":[{"min":0,"max":65536,"increment":8}],"tagLen":[128],"ivLen":[96],"ivGen":"internal","ivGenMode":"8.2.2","revision":"1.0"}, + {"algorithm":"CMAC-AES","capabilities":[{"direction":["gen","ver"],"msgLen":[{"min":0,"max":524288,"increment":8}],"keyLen":[128,256],"macLen":[{"min":8,"max":128,"increment":8}]}],"revision":"1.0"}, + + {"algorithm":"TLS-v1.2","mode":"KDF","revision":"RFC7627","hashAlg":["SHA2-256","SHA2-384","SHA2-512"]}, + {"algorithm":"TLS-v1.3","mode":"KDF","revision":"RFC8446","hmacAlg":["SHA2-256","SHA2-384"],"runningMode":["DHE","PSK","PSK-DHE"]}, + {"algorithm":"kdf-components","mode":"ssh","revision":"1.0","hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512"],"cipher":["AES-128","AES-192","AES-256"]}, + + {"algorithm":"KAS-ECC-SSC","revision":"Sp800-56Ar3","scheme":{"ephemeralUnified":{"kasRole":["initiator","responder"]},"staticUnified":{"kasRole":["initiator","responder"]}},"domainParameterGenerationMethods":["P-224","P-256","P-384","P-521"]}, + + {"algorithm":"KDF","revision":"1.0","capabilities":[{"kdfMode":"counter","macMode":["CMAC-AES128","CMAC-AES192","CMAC-AES256"],"supportedLengths":[256],"fixedDataOrder":["before fixed data"],"counterLength":[16]},{"kdfMode":"feedback","macMode":["HMAC-SHA2-224","HMAC-SHA2-256","HMAC-SHA2-384","HMAC-SHA2-512","HMAC-SHA2-512/224","HMAC-SHA2-512/256","HMAC-SHA3-224","HMAC-SHA3-256","HMAC-SHA3-384","HMAC-SHA3-512"],"customKeyInLength":0,"supportedLengths":[{"min":8,"max":4096,"increment":8}],"fixedDataOrder":["after fixed data"],"counterLength":[8],"supportsEmptyIv":true,"requiresEmptyIv":true}]}, + + {"algorithm":"RSA","mode":"keyGen","revision":"FIPS186-5","infoGeneratedByServer":true,"pubExpMode":"fixed","fixedPubExp":"010001","keyFormat":"standard","capabilities":[{"randPQ":"probable","properties":[{"modulo":2048,"primeTest":["2powSecStr"]},{"modulo":3072,"primeTest":["2powSecStr"]},{"modulo":4096,"primeTest":["2powSecStr"]}]}]}, + {"algorithm":"RSA","mode":"sigGen","revision":"FIPS186-5","capabilities":[{"sigType":"pkcs1v1.5","properties":[{"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]},{"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]},{"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]},{"maskFunction":["mgf1"],"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]},{"maskFunction":["mgf1"],"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]}]}, + {"algorithm":"RSA","mode":"sigVer","revision":"FIPS186-5","pubExpMode":"fixed","fixedPubExp":"010001","capabilities":[{"sigType":"pkcs1v1.5","properties":[{"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pkcs1v1.5","properties":[{"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pkcs1v1.5","properties":[{"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224"},{"hashAlg":"SHA2-256"},{"hashAlg":"SHA2-384"},{"hashAlg":"SHA2-512"}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":2048,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":3072,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]},{"sigType":"pss","properties":[{"maskFunction":["mgf1"],"modulo":4096,"hashPair":[{"hashAlg":"SHA2-224","saltLen":28},{"hashAlg":"SHA2-256","saltLen":32},{"hashAlg":"SHA2-384","saltLen":48},{"hashAlg":"SHA2-512","saltLen":64}]}]}]}, + + {"algorithm":"KTS-IFC","revision":"Sp800-56Br2","fixedPubExp":"010001","iutId":"C0FFEE","modulo":[2048,3072,4096],"keyGenerationMethods":["rsakpg1-basic"],"scheme":{"KTS-OAEP-basic":{"l":1024,"kasRole":["responder","initiator"],"ktsMethod":{"hashAlgs":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"],"supportsNullAssociatedData":true,"encoding":["concatenation"]}}}} +] diff --git a/src/crypto/internal/fips140test/acvp_test.config.json b/src/crypto/internal/fips140test/acvp_test.config.json index 49ab51d0d2a552..e14a28126715f3 100644 --- a/src/crypto/internal/fips140test/acvp_test.config.json +++ b/src/crypto/internal/fips140test/acvp_test.config.json @@ -11,6 +11,11 @@ {"Wrapper": "go", "In": "vectors/SHA3-384.bz2", "Out": "expected/SHA3-384.bz2"}, {"Wrapper": "go", "In": "vectors/SHA3-512.bz2", "Out": "expected/SHA3-512.bz2"}, + {"Wrapper": "go", "In": "vectors/SHAKE-128.bz2", "Out": "expected/SHAKE-128.bz2"}, + {"Wrapper": "go", "In": "vectors/SHAKE-256.bz2", "Out": "expected/SHAKE-256.bz2"}, + {"Wrapper": "go", "In": "vectors/cSHAKE-128.bz2", "Out": "expected/cSHAKE-128.bz2"}, + {"Wrapper": "go", "In": "vectors/cSHAKE-256.bz2", "Out": "expected/cSHAKE-256.bz2"}, + {"Wrapper": "go", "In": "vectors/HMAC-SHA2-224.bz2", "Out": "expected/HMAC-SHA2-224.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA2-256.bz2", "Out": "expected/HMAC-SHA2-256.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA2-384.bz2", "Out": "expected/HMAC-SHA2-384.bz2"}, @@ -18,10 +23,35 @@ {"Wrapper": "go", "In": "vectors/HMAC-SHA2-512-224.bz2", "Out": "expected/HMAC-SHA2-512-224.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA2-512-256.bz2", "Out": "expected/HMAC-SHA2-512-256.bz2"}, + {"Wrapper": "go", "In": "vectors/KDA.bz2", "Out": "expected/KDA.bz2"}, + {"Wrapper": "go", "In": "vectors/HMAC-SHA3-224.bz2", "Out": "expected/HMAC-SHA3-224.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA3-256.bz2", "Out": "expected/HMAC-SHA3-256.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA3-384.bz2", "Out": "expected/HMAC-SHA3-384.bz2"}, {"Wrapper": "go", "In": "vectors/HMAC-SHA3-512.bz2", "Out": "expected/HMAC-SHA3-512.bz2"}, - {"Wrapper": "go", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"} -] \ No newline at end of file + {"Wrapper": "go", "In": "vectors/PBKDF.bz2", "Out": "expected/PBKDF.bz2"}, + + {"Wrapper": "go", "In": "vectors/ML-KEM.bz2", "Out": "expected/ML-KEM.bz2"}, + + {"Wrapper": "go", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.bz2"}, + + {"Wrapper": "go", "In": "vectors/ctrDRBG.bz2", "Out": "expected/ctrDRBG.bz2"}, + + {"Wrapper": "go", "In": "vectors/EDDSA.bz2", "Out": "expected/EDDSA.bz2"}, + + {"Wrapper": "go", "In": "vectors/ECDSA.bz2", "Out": "expected/ECDSA.bz2"}, + + {"Wrapper": "go", "In": "vectors/ACVP-AES-CBC.bz2", "Out": "expected/ACVP-AES-CBC.bz2"}, + {"Wrapper": "go", "In": "vectors/ACVP-AES-CTR.bz2", "Out": "expected/ACVP-AES-CTR.bz2"}, + {"Wrapper": "go", "In": "vectors/ACVP-AES-GCM.bz2", "Out": "expected/ACVP-AES-GCM.bz2"}, + + {"Wrapper": "go", "In": "vectors/CMAC-AES.bz2", "Out": "expected/CMAC-AES.bz2"}, + + {"Wrapper": "go", "In": "vectors/TLS-v1.2.bz2", "Out": "expected/TLS-v1.2.bz2"}, + {"Wrapper": "go", "In": "vectors/TLS-v1.3.bz2", "Out": "expected/TLS-v1.3.bz2"}, + + {"Wrapper": "go", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"}, + + {"Wrapper": "go", "In": "vectors/RSA.bz2", "Out": "expected/RSA.bz2"} +] diff --git a/src/crypto/internal/fips140test/acvp_test.go b/src/crypto/internal/fips140test/acvp_test.go index 139655ecf60389..ddb234bab655e1 100644 --- a/src/crypto/internal/fips140test/acvp_test.go +++ b/src/crypto/internal/fips140test/acvp_test.go @@ -21,26 +21,52 @@ package fipstest import ( "bufio" "bytes" + "crypto/elliptic" "crypto/internal/cryptotest" "crypto/internal/fips140" + "crypto/internal/fips140/aes" + "crypto/internal/fips140/aes/gcm" + "crypto/internal/fips140/bigmod" + "crypto/internal/fips140/drbg" + "crypto/internal/fips140/ecdh" + "crypto/internal/fips140/ecdsa" + "crypto/internal/fips140/ed25519" + "crypto/internal/fips140/edwards25519" + "crypto/internal/fips140/hkdf" "crypto/internal/fips140/hmac" + "crypto/internal/fips140/mlkem" "crypto/internal/fips140/pbkdf2" + "crypto/internal/fips140/rsa" "crypto/internal/fips140/sha256" "crypto/internal/fips140/sha3" "crypto/internal/fips140/sha512" + "crypto/internal/fips140/ssh" + "crypto/internal/fips140/subtle" + "crypto/internal/fips140/tls12" + "crypto/internal/fips140/tls13" + "crypto/internal/impl" + "crypto/rand" _ "embed" "encoding/binary" "errors" "fmt" "internal/testenv" "io" + "math/big" "os" "path/filepath" "strings" "testing" ) +var noPAAPAI = os.Getenv("GONOPAAPAI") == "1" + func TestMain(m *testing.M) { + if noPAAPAI { + for _, p := range impl.Packages() { + impl.Select(p, "") + } + } if os.Getenv("ACVP_WRAPPER") == "1" { wrapperMain() } else { @@ -49,6 +75,10 @@ func TestMain(m *testing.M) { } func wrapperMain() { + if !fips140.Enabled { + fmt.Fprintln(os.Stderr, "ACVP wrapper must be run with GODEBUG=fips140=on") + os.Exit(2) + } if err := processingLoop(bufio.NewReader(os.Stdin), os.Stdout); err != nil { fmt.Fprintf(os.Stderr, "processing error: %v\n", err) os.Exit(1) @@ -68,13 +98,59 @@ type command struct { handler commandHandler } +type ecdsaSigType int + +const ( + ecdsaSigTypeNormal ecdsaSigType = iota + ecdsaSigTypeDeterministic +) + +type aesDirection int + +const ( + aesEncrypt aesDirection = iota + aesDecrypt +) + var ( // SHA2 algorithm capabilities: // https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#section-7.2 + // SHA3 and SHAKE algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-sha3.html#name-sha3-and-shake-algorithm-ca + // cSHAKE algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-xof.html#section-7.2 // HMAC algorithm capabilities: // https://pages.nist.gov/ACVP/draft-fussell-acvp-mac.html#section-7 // PBKDF2 algorithm capabilities: // https://pages.nist.gov/ACVP/draft-celi-acvp-pbkdf.html#section-7.3 + // ML-KEM algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-ml-kem.html#section-7.3 + // HMAC DRBG algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2 + // EDDSA algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-7 + // ECDSA and DetECDSA algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-fussell-acvp-ecdsa.html#section-7 + // AES algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-symmetric.html#section-7.3 + // HKDF KDA algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-kdf-hkdf.html#section-7.3 + // OneStepNoCounter KDA algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-kdf-onestepnocounter.html#section-7.2 + // TLS 1.2 KDF algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-tls.html#section-7.2 + // TLS 1.3 KDF algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-hammett-acvp-kdf-tls-v1.3.html#section-7.2 + // SSH KDF algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#section-7.2 + // ECDH algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ssc-ecc.html#section-7.3 + // HMAC DRBG and CTR DRBG algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2 + // KDF-Counter and KDF-Feedback algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-kbkdf.html#section-7.3 + // RSA algorithm capabilities: + // https://pages.nist.gov/ACVP/draft-celi-acvp-rsa.html#section-7.3 //go:embed acvp_capabilities.json capabilitiesJson []byte @@ -106,6 +182,22 @@ var ( "SHA3-512": cmdHashAft(sha3.New512()), "SHA3-512/MCT": cmdSha3Mct(sha3.New512()), + // Note: SHAKE AFT and VOT test types can be handled by the same command + // handler impl, but use distinct acvptool command names, and so are + // registered twice with the same digest: once under "SHAKE-xxx" for AFT, + // and once under"SHAKE-xxx/VOT" for VOT. + "SHAKE-128": cmdShakeAftVot(sha3.NewShake128()), + "SHAKE-128/VOT": cmdShakeAftVot(sha3.NewShake128()), + "SHAKE-128/MCT": cmdShakeMct(sha3.NewShake128()), + "SHAKE-256": cmdShakeAftVot(sha3.NewShake256()), + "SHAKE-256/VOT": cmdShakeAftVot(sha3.NewShake256()), + "SHAKE-256/MCT": cmdShakeMct(sha3.NewShake256()), + + "cSHAKE-128": cmdCShakeAft(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake128(N, S) }), + "cSHAKE-128/MCT": cmdCShakeMct(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake128(N, S) }), + "cSHAKE-256": cmdCShakeAft(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake256(N, S) }), + "cSHAKE-256/MCT": cmdCShakeMct(func(N, S []byte) *sha3.SHAKE { return sha3.NewCShake256(N, S) }), + "HMAC-SHA2-224": cmdHmacAft(func() fips140.Hash { return sha256.New224() }), "HMAC-SHA2-256": cmdHmacAft(func() fips140.Hash { return sha256.New() }), "HMAC-SHA2-384": cmdHmacAft(func() fips140.Hash { return sha512.New384() }), @@ -117,7 +209,144 @@ var ( "HMAC-SHA3-384": cmdHmacAft(func() fips140.Hash { return sha3.New384() }), "HMAC-SHA3-512": cmdHmacAft(func() fips140.Hash { return sha3.New512() }), + "HKDF/SHA2-224": cmdHkdfAft(func() fips140.Hash { return sha256.New224() }), + "HKDF/SHA2-256": cmdHkdfAft(func() fips140.Hash { return sha256.New() }), + "HKDF/SHA2-384": cmdHkdfAft(func() fips140.Hash { return sha512.New384() }), + "HKDF/SHA2-512": cmdHkdfAft(func() fips140.Hash { return sha512.New() }), + "HKDF/SHA2-512/224": cmdHkdfAft(func() fips140.Hash { return sha512.New512_224() }), + "HKDF/SHA2-512/256": cmdHkdfAft(func() fips140.Hash { return sha512.New512_256() }), + "HKDF/SHA3-224": cmdHkdfAft(func() fips140.Hash { return sha3.New224() }), + "HKDF/SHA3-256": cmdHkdfAft(func() fips140.Hash { return sha3.New256() }), + "HKDF/SHA3-384": cmdHkdfAft(func() fips140.Hash { return sha3.New384() }), + "HKDF/SHA3-512": cmdHkdfAft(func() fips140.Hash { return sha3.New512() }), + + "HKDFExtract/SHA2-256": cmdHkdfExtractAft(func() fips140.Hash { return sha256.New() }), + "HKDFExtract/SHA2-384": cmdHkdfExtractAft(func() fips140.Hash { return sha512.New384() }), + "HKDFExpandLabel/SHA2-256": cmdHkdfExpandLabelAft(func() fips140.Hash { return sha256.New() }), + "HKDFExpandLabel/SHA2-384": cmdHkdfExpandLabelAft(func() fips140.Hash { return sha512.New384() }), + "PBKDF": cmdPbkdf(), + + "ML-KEM-768/keyGen": cmdMlKem768KeyGenAft(), + "ML-KEM-768/encap": cmdMlKem768EncapAft(), + "ML-KEM-768/decap": cmdMlKem768DecapAft(), + "ML-KEM-1024/keyGen": cmdMlKem1024KeyGenAft(), + "ML-KEM-1024/encap": cmdMlKem1024EncapAft(), + "ML-KEM-1024/decap": cmdMlKem1024DecapAft(), + + "hmacDRBG/SHA2-224": cmdHmacDrbgAft(func() fips140.Hash { return sha256.New224() }), + "hmacDRBG/SHA2-256": cmdHmacDrbgAft(func() fips140.Hash { return sha256.New() }), + "hmacDRBG/SHA2-384": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New384() }), + "hmacDRBG/SHA2-512": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New() }), + "hmacDRBG/SHA2-512/224": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New512_224() }), + "hmacDRBG/SHA2-512/256": cmdHmacDrbgAft(func() fips140.Hash { return sha512.New512_256() }), + "hmacDRBG/SHA3-224": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New224() }), + "hmacDRBG/SHA3-256": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New256() }), + "hmacDRBG/SHA3-384": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New384() }), + "hmacDRBG/SHA3-512": cmdHmacDrbgAft(func() fips140.Hash { return sha3.New512() }), + + "EDDSA/keyGen": cmdEddsaKeyGenAft(), + "EDDSA/keyVer": cmdEddsaKeyVerAft(), + "EDDSA/sigGen": cmdEddsaSigGenAftBft(), + "EDDSA/sigVer": cmdEddsaSigVerAft(), + + "ECDSA/keyGen": cmdEcdsaKeyGenAft(), + "ECDSA/keyVer": cmdEcdsaKeyVerAft(), + "ECDSA/sigGen": cmdEcdsaSigGenAft(ecdsaSigTypeNormal), + "ECDSA/sigVer": cmdEcdsaSigVerAft(), + "DetECDSA/sigGen": cmdEcdsaSigGenAft(ecdsaSigTypeDeterministic), + + "AES-CBC/encrypt": cmdAesCbc(aesEncrypt), + "AES-CBC/decrypt": cmdAesCbc(aesDecrypt), + "AES-CTR/encrypt": cmdAesCtr(aesEncrypt), + "AES-CTR/decrypt": cmdAesCtr(aesDecrypt), + "AES-GCM/seal": cmdAesGcmSeal(false), + "AES-GCM/open": cmdAesGcmOpen(false), + "AES-GCM-randnonce/seal": cmdAesGcmSeal(true), + "AES-GCM-randnonce/open": cmdAesGcmOpen(true), + + "CMAC-AES": cmdCmacAesAft(), + "CMAC-AES/verify": cmdCmacAesVerifyAft(), + + // Note: Only SHA2-256, SHA2-384 and SHA2-512 are valid hash functions for TLSKDF. + // See https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-tls.html#section-7.2.1 + "TLSKDF/1.2/SHA2-256": cmdTlsKdf12Aft(func() fips140.Hash { return sha256.New() }), + "TLSKDF/1.2/SHA2-384": cmdTlsKdf12Aft(func() fips140.Hash { return sha512.New384() }), + "TLSKDF/1.2/SHA2-512": cmdTlsKdf12Aft(func() fips140.Hash { return sha512.New() }), + + // Note: only SHA2-224, SHA2-256, SHA2-384 and SHA2-512 are valid hash functions for SSHKDF. + // See https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#section-7.2.1 + "SSHKDF/SHA2-224/client": cmdSshKdfAft(func() fips140.Hash { return sha256.New224() }, ssh.ClientKeys), + "SSHKDF/SHA2-224/server": cmdSshKdfAft(func() fips140.Hash { return sha256.New224() }, ssh.ServerKeys), + "SSHKDF/SHA2-256/client": cmdSshKdfAft(func() fips140.Hash { return sha256.New() }, ssh.ClientKeys), + "SSHKDF/SHA2-256/server": cmdSshKdfAft(func() fips140.Hash { return sha256.New() }, ssh.ServerKeys), + "SSHKDF/SHA2-384/client": cmdSshKdfAft(func() fips140.Hash { return sha512.New384() }, ssh.ClientKeys), + "SSHKDF/SHA2-384/server": cmdSshKdfAft(func() fips140.Hash { return sha512.New384() }, ssh.ServerKeys), + "SSHKDF/SHA2-512/client": cmdSshKdfAft(func() fips140.Hash { return sha512.New() }, ssh.ClientKeys), + "SSHKDF/SHA2-512/server": cmdSshKdfAft(func() fips140.Hash { return sha512.New() }, ssh.ServerKeys), + + "ECDH/P-224": cmdEcdhAftVal(ecdh.P224()), + "ECDH/P-256": cmdEcdhAftVal(ecdh.P256()), + "ECDH/P-384": cmdEcdhAftVal(ecdh.P384()), + "ECDH/P-521": cmdEcdhAftVal(ecdh.P521()), + + "ctrDRBG/AES-256": cmdCtrDrbgAft(), + "ctrDRBG-reseed/AES-256": cmdCtrDrbgReseedAft(), + + "RSA/keyGen": cmdRsaKeyGenAft(), + + "RSA/sigGen/SHA2-224/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", false), + "RSA/sigGen/SHA2-256/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New() }, "SHA-256", false), + "RSA/sigGen/SHA2-384/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", false), + "RSA/sigGen/SHA2-512/pkcs1v1.5": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New() }, "SHA-512", false), + "RSA/sigGen/SHA2-224/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", true), + "RSA/sigGen/SHA2-256/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha256.New() }, "SHA-256", true), + "RSA/sigGen/SHA2-384/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", true), + "RSA/sigGen/SHA2-512/pss": cmdRsaSigGenAft(func() fips140.Hash { return sha512.New() }, "SHA-512", true), + + "RSA/sigVer/SHA2-224/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", false), + "RSA/sigVer/SHA2-256/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New() }, "SHA-256", false), + "RSA/sigVer/SHA2-384/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", false), + "RSA/sigVer/SHA2-512/pkcs1v1.5": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New() }, "SHA-512", false), + "RSA/sigVer/SHA2-224/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New224() }, "SHA-224", true), + "RSA/sigVer/SHA2-256/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha256.New() }, "SHA-256", true), + "RSA/sigVer/SHA2-384/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New384() }, "SHA-384", true), + "RSA/sigVer/SHA2-512/pss": cmdRsaSigVerAft(func() fips140.Hash { return sha512.New() }, "SHA-512", true), + + "KDF-counter": cmdKdfCounterAft(), + "KDF-feedback": cmdKdfFeedbackAft(), + + "OneStepNoCounter/HMAC-SHA2-224": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha256.New224() }), + "OneStepNoCounter/HMAC-SHA2-256": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha256.New() }), + "OneStepNoCounter/HMAC-SHA2-384": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha512.New384() }), + "OneStepNoCounter/HMAC-SHA2-512": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha512.New() }), + "OneStepNoCounter/HMAC-SHA2-512/224": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha512.New512_224() }), + "OneStepNoCounter/HMAC-SHA2-512/256": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha512.New512_256() }), + "OneStepNoCounter/HMAC-SHA3-224": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha3.New224() }), + "OneStepNoCounter/HMAC-SHA3-256": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha3.New256() }), + "OneStepNoCounter/HMAC-SHA3-384": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha3.New384() }), + "OneStepNoCounter/HMAC-SHA3-512": cmdOneStepNoCounterHmacAft(func() fips140.Hash { return sha3.New512() }), + + "KTS-IFC/SHA2-224/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha256.New224() }), + "KTS-IFC/SHA2-224/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha256.New224() }), + "KTS-IFC/SHA2-256/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha256.New() }), + "KTS-IFC/SHA2-256/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha256.New() }), + "KTS-IFC/SHA2-384/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha512.New384() }), + "KTS-IFC/SHA2-384/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha512.New384() }), + "KTS-IFC/SHA2-512/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha512.New() }), + "KTS-IFC/SHA2-512/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha512.New() }), + "KTS-IFC/SHA2-512/224/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha512.New512_224() }), + "KTS-IFC/SHA2-512/224/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha512.New512_224() }), + "KTS-IFC/SHA2-512/256/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha512.New512_256() }), + "KTS-IFC/SHA2-512/256/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha512.New512_256() }), + "KTS-IFC/SHA3-224/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha3.New224() }), + "KTS-IFC/SHA3-224/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha3.New224() }), + "KTS-IFC/SHA3-256/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha3.New256() }), + "KTS-IFC/SHA3-256/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha3.New256() }), + "KTS-IFC/SHA3-384/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha3.New384() }), + "KTS-IFC/SHA3-384/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha3.New384() }), + "KTS-IFC/SHA3-512/initiator": cmdKtsIfcInitiatorAft(func() fips140.Hash { return sha3.New512() }), + "KTS-IFC/SHA3-512/responder": cmdKtsIfcResponderAft(func() fips140.Hash { return sha3.New512() }), } ) @@ -335,6 +564,154 @@ func cmdSha3Mct(h fips140.Hash) command { } } +func cmdShakeAftVot(h *sha3.SHAKE) command { + return command{ + requiredArgs: 2, // Message, output length (bytes) + handler: func(args [][]byte) ([][]byte, error) { + msg := args[0] + + outLenBytes := binary.LittleEndian.Uint32(args[1]) + digest := make([]byte, outLenBytes) + + h.Reset() + h.Write(msg) + h.Read(digest) + + return [][]byte{digest}, nil + }, + } +} + +func cmdShakeMct(h *sha3.SHAKE) command { + return command{ + requiredArgs: 4, // Seed message, min output length (bytes), max output length (bytes), output length (bytes) + handler: func(args [][]byte) ([][]byte, error) { + md := args[0] + minOutBytes := binary.LittleEndian.Uint32(args[1]) + maxOutBytes := binary.LittleEndian.Uint32(args[2]) + + outputLenBytes := binary.LittleEndian.Uint32(args[3]) + if outputLenBytes < 2 { + return nil, fmt.Errorf("invalid output length: %d", outputLenBytes) + } + + rangeBytes := maxOutBytes - minOutBytes + 1 + if rangeBytes == 0 { + return nil, fmt.Errorf("invalid maxOutBytes and minOutBytes: %d, %d", maxOutBytes, minOutBytes) + } + + for i := 0; i < 1000; i++ { + // "The MSG[i] input to SHAKE MUST always contain at least 128 bits. If this is not the case + // as the previous digest was too short, append empty bits to the rightmost side of the digest." + boundary := min(len(md), 16) + msg := make([]byte, 16) + copy(msg, md[:boundary]) + + // MD[i] = SHAKE(MSG[i], OutputLen * 8) + h.Reset() + h.Write(msg) + digest := make([]byte, outputLenBytes) + h.Read(digest) + md = digest + + // RightmostOutputBits = 16 rightmost bits of MD[i] as an integer + // OutputLen = minOutBytes + (RightmostOutputBits % Range) + rightmostOutput := uint32(md[outputLenBytes-2])<<8 | uint32(md[outputLenBytes-1]) + outputLenBytes = minOutBytes + (rightmostOutput % rangeBytes) + } + + encodedOutputLenBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(encodedOutputLenBytes, outputLenBytes) + + return [][]byte{md, encodedOutputLenBytes}, nil + }, + } +} + +func cmdCShakeAft(hFn func(N, S []byte) *sha3.SHAKE) command { + return command{ + requiredArgs: 4, // Message, output length bytes, function name, customization + handler: func(args [][]byte) ([][]byte, error) { + msg := args[0] + outLenBytes := binary.LittleEndian.Uint32(args[1]) + functionName := args[2] + customization := args[3] + + h := hFn(functionName, customization) + h.Write(msg) + + out := make([]byte, outLenBytes) + h.Read(out) + + return [][]byte{out}, nil + }, + } +} + +func cmdCShakeMct(hFn func(N, S []byte) *sha3.SHAKE) command { + return command{ + requiredArgs: 6, // Message, min output length (bits), max output length (bits), output length (bits), increment (bits), customization + handler: func(args [][]byte) ([][]byte, error) { + message := args[0] + minOutLenBytes := binary.LittleEndian.Uint32(args[1]) + maxOutLenBytes := binary.LittleEndian.Uint32(args[2]) + outputLenBytes := binary.LittleEndian.Uint32(args[3]) + incrementBytes := binary.LittleEndian.Uint32(args[4]) + customization := args[5] + + if outputLenBytes < 2 { + return nil, fmt.Errorf("invalid output length: %d", outputLenBytes) + } + + rangeBits := (maxOutLenBytes*8 - minOutLenBytes*8) + 1 + if rangeBits == 0 { + return nil, fmt.Errorf("invalid maxOutLenBytes and minOutLenBytes: %d, %d", maxOutLenBytes, minOutLenBytes) + } + + // cSHAKE Monte Carlo test inner loop: + // https://pages.nist.gov/ACVP/draft-celi-acvp-xof.html#section-6.2.1 + for i := 0; i < 1000; i++ { + // InnerMsg = Left(Output[i-1] || ZeroBits(128), 128); + boundary := min(len(message), 16) + innerMsg := make([]byte, 16) + copy(innerMsg, message[:boundary]) + + // Output[i] = CSHAKE(InnerMsg, OutputLen, FunctionName, Customization); + h := hFn(nil, customization) // Note: function name fixed to "" for MCT. + h.Write(innerMsg) + digest := make([]byte, outputLenBytes) + h.Read(digest) + message = digest + + // Rightmost_Output_bits = Right(Output[i], 16); + rightmostOutput := digest[outputLenBytes-2:] + // IMPORTANT: the specification says: + // NOTE: For the "Rightmost_Output_bits % Range" operation, the Rightmost_Output_bits bit string + // should be interpretted as a little endian-encoded number. + // This is **a lie**! It has to be interpreted as a big-endian number. + rightmostOutputBE := binary.BigEndian.Uint16(rightmostOutput) + + // OutputLen = MinOutLen + (floor((Rightmost_Output_bits % Range) / OutLenIncrement) * OutLenIncrement); + incrementBits := incrementBytes * 8 + outputLenBits := (minOutLenBytes * 8) + (((uint32)(rightmostOutputBE)%rangeBits)/incrementBits)*incrementBits + outputLenBytes = outputLenBits / 8 + + // Customization = BitsToString(InnerMsg || Rightmost_Output_bits); + msgWithBits := append(innerMsg, rightmostOutput...) + customization = make([]byte, len(msgWithBits)) + for i, b := range msgWithBits { + customization[i] = (b % 26) + 65 + } + } + + encodedOutputLenBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(encodedOutputLenBytes, outputLenBytes) + + return [][]byte{message, encodedOutputLenBytes, customization}, nil + }, + } +} + func cmdHmacAft(h func() fips140.Hash) command { return command{ requiredArgs: 2, // Message and key @@ -348,6 +725,46 @@ func cmdHmacAft(h func() fips140.Hash) command { } } +func cmdHkdfAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 4, // Key, salt, info, length bytes + handler: func(args [][]byte) ([][]byte, error) { + key := args[0] + salt := args[1] + info := args[2] + keyLen := int(binary.LittleEndian.Uint32(args[3])) + + return [][]byte{hkdf.Key(h, key, salt, string(info), keyLen)}, nil + }, + } +} + +func cmdHkdfExtractAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 2, // secret, salt + handler: func(args [][]byte) ([][]byte, error) { + secret := args[0] + salt := args[1] + + return [][]byte{hkdf.Extract(h, secret, salt)}, nil + }, + } +} + +func cmdHkdfExpandLabelAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 4, // output length, secret, label, transcript hash + handler: func(args [][]byte) ([][]byte, error) { + keyLen := int(binary.LittleEndian.Uint32(args[0])) + secret := args[1] + label := args[2] + transcriptHash := args[3] + + return [][]byte{tls13.ExpandLabel(h, secret, string(label), transcriptHash, keyLen)}, nil + }, + } +} + func cmdPbkdf() command { return command{ // Hash name, key length, salt, password, iteration count @@ -373,6 +790,336 @@ func cmdPbkdf() command { } } +func cmdEddsaKeyGenAft() command { + return command{ + requiredArgs: 1, // Curve name + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + sk, err := ed25519.GenerateKey() + if err != nil { + return nil, fmt.Errorf("generating EDDSA keypair: %w", err) + } + + // EDDSA/keyGen/AFT responses are d & q, described[0] as: + // d The encoded private key point + // q The encoded public key point + // + // Contrary to the description of a "point", d is the private key + // seed bytes per FIPS.186-5[1] A.2.3. + // + // [0]: https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-9.1 + // [1]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf + return [][]byte{sk.Seed(), sk.PublicKey()}, nil + }, + } +} + +func cmdEddsaKeyVerAft() command { + return command{ + requiredArgs: 2, // Curve name, Q + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + // Verify the point is on the curve. The higher-level ed25519 API does + // this at signature verification time so we have to use the lower-level + // edwards25519 package to do it here in absence of a signature to verify. + if _, err := new(edwards25519.Point).SetBytes(args[1]); err != nil { + return [][]byte{{0}}, nil + } + + return [][]byte{{1}}, nil + }, + } +} + +func cmdEddsaSigGenAftBft() command { + return command{ + requiredArgs: 5, // Curve name, private key seed, message, prehash, context + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + sk, err := ed25519.NewPrivateKeyFromSeed(args[1]) + if err != nil { + return nil, fmt.Errorf("error creating private key: %w", err) + } + msg := args[2] + prehash := args[3] + context := string(args[4]) + + var sig []byte + if prehash[0] == 1 { + h := sha512.New() + h.Write(msg) + msg = h.Sum(nil) + + // With ed25519 the context is only specified for sigGen tests when using prehashing. + // See https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-8.6 + sig, err = ed25519.SignPH(sk, msg, context) + if err != nil { + return nil, fmt.Errorf("error signing message: %w", err) + } + } else { + sig = ed25519.Sign(sk, msg) + } + + return [][]byte{sig}, nil + }, + } +} + +func cmdEddsaSigVerAft() command { + return command{ + requiredArgs: 5, // Curve name, message, public key, signature, prehash + handler: func(args [][]byte) ([][]byte, error) { + if string(args[0]) != "ED-25519" { + return nil, fmt.Errorf("unsupported EDDSA curve: %q", args[0]) + } + + msg := args[1] + pk, err := ed25519.NewPublicKey(args[2]) + if err != nil { + return nil, fmt.Errorf("invalid public key: %w", err) + } + sig := args[3] + prehash := args[4] + + if prehash[0] == 1 { + h := sha512.New() + h.Write(msg) + msg = h.Sum(nil) + // Context is only specified for sigGen, not sigVer. + // See https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-8.6 + err = ed25519.VerifyPH(pk, msg, sig, "") + } else { + err = ed25519.Verify(pk, msg, sig) + } + + if err != nil { + return [][]byte{{0}}, nil + } + + return [][]byte{{1}}, nil + }, + } +} + +func cmdEcdsaKeyGenAft() command { + return command{ + requiredArgs: 1, // Curve name + handler: func(args [][]byte) ([][]byte, error) { + curve, err := lookupCurve(string(args[0])) + if err != nil { + return nil, err + } + + var sk *ecdsa.PrivateKey + switch curve.Params() { + case elliptic.P224().Params(): + sk, err = ecdsa.GenerateKey(ecdsa.P224(), rand.Reader) + case elliptic.P256().Params(): + sk, err = ecdsa.GenerateKey(ecdsa.P256(), rand.Reader) + case elliptic.P384().Params(): + sk, err = ecdsa.GenerateKey(ecdsa.P384(), rand.Reader) + case elliptic.P521().Params(): + sk, err = ecdsa.GenerateKey(ecdsa.P521(), rand.Reader) + default: + return nil, fmt.Errorf("unsupported curve: %v", curve) + } + + if err != nil { + return nil, err + } + + pubBytes := sk.PublicKey().Bytes() + byteLen := (curve.Params().BitSize + 7) / 8 + + return [][]byte{ + sk.Bytes(), + pubBytes[1 : 1+byteLen], + pubBytes[1+byteLen:], + }, nil + }, + } +} + +func cmdEcdsaKeyVerAft() command { + return command{ + requiredArgs: 3, // Curve name, X, Y + handler: func(args [][]byte) ([][]byte, error) { + curve, err := lookupCurve(string(args[0])) + if err != nil { + return nil, err + } + + x := new(big.Int).SetBytes(args[1]) + y := new(big.Int).SetBytes(args[2]) + + if curve.IsOnCurve(x, y) { + return [][]byte{{1}}, nil + } + + return [][]byte{{0}}, nil + }, + } +} + +// pointFromAffine is used to convert the PublicKey to a nistec SetBytes input. +// Duplicated from crypto/ecdsa.go's pointFromAffine. +func pointFromAffine(curve elliptic.Curve, x, y *big.Int) ([]byte, error) { + bitSize := curve.Params().BitSize + // Reject values that would not get correctly encoded. + if x.Sign() < 0 || y.Sign() < 0 { + return nil, errors.New("negative coordinate") + } + if x.BitLen() > bitSize || y.BitLen() > bitSize { + return nil, errors.New("overflowing coordinate") + } + // Encode the coordinates and let SetBytes reject invalid points. + byteLen := (bitSize + 7) / 8 + buf := make([]byte, 1+2*byteLen) + buf[0] = 4 // uncompressed point + x.FillBytes(buf[1 : 1+byteLen]) + y.FillBytes(buf[1+byteLen : 1+2*byteLen]) + return buf, nil +} + +func signEcdsa[P ecdsa.Point[P], H fips140.Hash](c *ecdsa.Curve[P], h func() H, sigType ecdsaSigType, q []byte, sk []byte, digest []byte) (*ecdsa.Signature, error) { + priv, err := ecdsa.NewPrivateKey(c, sk, q) + if err != nil { + return nil, fmt.Errorf("invalid private key: %w", err) + } + + var sig *ecdsa.Signature + switch sigType { + case ecdsaSigTypeNormal: + sig, err = ecdsa.Sign(c, h, priv, rand.Reader, digest) + case ecdsaSigTypeDeterministic: + sig, err = ecdsa.SignDeterministic(c, h, priv, digest) + default: + return nil, fmt.Errorf("unsupported signature type: %v", sigType) + } + if err != nil { + return nil, fmt.Errorf("signing failed: %w", err) + } + + return sig, nil +} + +func cmdEcdsaSigGenAft(sigType ecdsaSigType) command { + return command{ + requiredArgs: 4, // Curve name, private key, hash name, message + handler: func(args [][]byte) ([][]byte, error) { + curve, err := lookupCurve(string(args[0])) + if err != nil { + return nil, err + } + + sk := args[1] + + newH, err := lookupHash(string(args[2])) + if err != nil { + return nil, err + } + + msg := args[3] + hashFunc := newH() + hashFunc.Write(msg) + digest := hashFunc.Sum(nil) + + d := new(big.Int).SetBytes(sk) + x, y := curve.ScalarBaseMult(d.Bytes()) + q, err := pointFromAffine(curve, x, y) + if err != nil { + return nil, err + } + + var sig *ecdsa.Signature + switch curve.Params() { + case elliptic.P224().Params(): + sig, err = signEcdsa(ecdsa.P224(), newH, sigType, q, sk, digest) + case elliptic.P256().Params(): + sig, err = signEcdsa(ecdsa.P256(), newH, sigType, q, sk, digest) + case elliptic.P384().Params(): + sig, err = signEcdsa(ecdsa.P384(), newH, sigType, q, sk, digest) + case elliptic.P521().Params(): + sig, err = signEcdsa(ecdsa.P521(), newH, sigType, q, sk, digest) + default: + return nil, fmt.Errorf("unsupported curve: %v", curve) + } + if err != nil { + return nil, err + } + + return [][]byte{sig.R, sig.S}, nil + }, + } +} + +func cmdEcdsaSigVerAft() command { + return command{ + requiredArgs: 7, // Curve name, hash name, message, X, Y, R, S + handler: func(args [][]byte) ([][]byte, error) { + curve, err := lookupCurve(string(args[0])) + if err != nil { + return nil, err + } + + newH, err := lookupHash(string(args[1])) + if err != nil { + return nil, err + } + + msg := args[2] + hashFunc := newH() + hashFunc.Write(msg) + digest := hashFunc.Sum(nil) + + x, y := args[3], args[4] + q, err := pointFromAffine(curve, new(big.Int).SetBytes(x), new(big.Int).SetBytes(y)) + if err != nil { + return nil, fmt.Errorf("invalid x/y coordinates: %v", err) + } + + signature := &ecdsa.Signature{R: args[5], S: args[6]} + + switch curve.Params() { + case elliptic.P224().Params(): + err = verifyEcdsa(ecdsa.P224(), q, digest, signature) + case elliptic.P256().Params(): + err = verifyEcdsa(ecdsa.P256(), q, digest, signature) + case elliptic.P384().Params(): + err = verifyEcdsa(ecdsa.P384(), q, digest, signature) + case elliptic.P521().Params(): + err = verifyEcdsa(ecdsa.P521(), q, digest, signature) + default: + return nil, fmt.Errorf("unsupported curve: %v", curve) + } + + if err == nil { + return [][]byte{{1}}, nil + } + + return [][]byte{{0}}, nil + }, + } +} + +func verifyEcdsa[P ecdsa.Point[P]](c *ecdsa.Curve[P], q []byte, digest []byte, sig *ecdsa.Signature) error { + pub, err := ecdsa.NewPublicKey(c, q) + if err != nil { + return fmt.Errorf("invalid public key: %w", err) + } + + return ecdsa.Verify(c, pub, digest, sig) +} + func lookupHash(name string) (func() fips140.Hash, error) { var h func() fips140.Hash @@ -404,37 +1151,955 @@ func lookupHash(name string) (func() fips140.Hash, error) { return h, nil } -func TestACVP(t *testing.T) { - testenv.SkipIfShortAndSlow(t) - - const ( - bsslModule = "boringssl.googlesource.com/boringssl.git" - bsslVersion = "v0.0.0-20241015160643-2587c4974dbe" - goAcvpModule = "github.com/cpu/go-acvp" - goAcvpVersion = "v0.0.0-20241011151719-6e0509dcb7ce" - ) +func cmdMlKem768KeyGenAft() command { + return command{ + requiredArgs: 1, // Seed + handler: func(args [][]byte) ([][]byte, error) { + seed := args[0] - // In crypto/tls/bogo_shim_test.go the test is skipped if run on a builder with runtime.GOOS == "windows" - // due to flaky networking. It may be necessary to do the same here. + dk, err := mlkem.NewDecapsulationKey768(seed) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 768 decapsulation key: %w", err) + } - // Stat the acvp test config file so the test will be re-run if it changes, invalidating cached results - // from the old config. - if _, err := os.Stat("acvp_test.config.json"); err != nil { - t.Fatalf("failed to stat config file: %s", err) + // Important: we must return the full encoding of dk, not the seed. + return [][]byte{dk.EncapsulationKey().Bytes(), mlkem.TestingOnlyExpandedBytes768(dk)}, nil + }, } +} - // Fetch the BSSL module and use the JSON output to find the absolute path to the dir. - bsslDir := cryptotest.FetchModule(t, bsslModule, bsslVersion) +func cmdMlKem768EncapAft() command { + return command{ + requiredArgs: 2, // Public key, entropy + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + entropy := args[1] - t.Log("building acvptool") + ek, err := mlkem.NewEncapsulationKey768(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 768 encapsulation key: %w", err) + } - // Build the acvptool binary. - toolPath := filepath.Join(t.TempDir(), "acvptool.exe") - goTool := testenv.GoToolPath(t) - cmd := testenv.Command(t, goTool, - "build", - "-o", toolPath, - "./util/fipstools/acvp/acvptool") + if len(entropy) != 32 { + return nil, fmt.Errorf("wrong entropy length: got %d, want 32", len(entropy)) + } + + sharedKey, ct := ek.EncapsulateInternal((*[32]byte)(entropy[:32])) + + return [][]byte{ct, sharedKey}, nil + }, + } +} + +func cmdMlKem768DecapAft() command { + return command{ + requiredArgs: 2, // Private key, ciphertext + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + ct := args[1] + + dk, err := mlkem.TestingOnlyNewDecapsulationKey768(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 768 decapsulation key: %w", err) + } + + sharedKey, err := dk.Decapsulate(ct) + if err != nil { + return nil, fmt.Errorf("decapsulating ML-KEM 768 ciphertext: %w", err) + } + + return [][]byte{sharedKey}, nil + }, + } +} + +func cmdMlKem1024KeyGenAft() command { + return command{ + requiredArgs: 1, // Seed + handler: func(args [][]byte) ([][]byte, error) { + seed := args[0] + + dk, err := mlkem.NewDecapsulationKey1024(seed) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 1024 decapsulation key: %w", err) + } + + // Important: we must return the full encoding of dk, not the seed. + return [][]byte{dk.EncapsulationKey().Bytes(), mlkem.TestingOnlyExpandedBytes1024(dk)}, nil + }, + } +} + +func cmdMlKem1024EncapAft() command { + return command{ + requiredArgs: 2, // Public key, entropy + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + entropy := args[1] + + ek, err := mlkem.NewEncapsulationKey1024(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 1024 encapsulation key: %w", err) + } + + if len(entropy) != 32 { + return nil, fmt.Errorf("wrong entropy length: got %d, want 32", len(entropy)) + } + + sharedKey, ct := ek.EncapsulateInternal((*[32]byte)(entropy[:32])) + + return [][]byte{ct, sharedKey}, nil + }, + } +} + +func cmdMlKem1024DecapAft() command { + return command{ + requiredArgs: 2, // Private key, ciphertext + handler: func(args [][]byte) ([][]byte, error) { + pk := args[0] + ct := args[1] + + dk, err := mlkem.TestingOnlyNewDecapsulationKey1024(pk) + if err != nil { + return nil, fmt.Errorf("generating ML-KEM 1024 decapsulation key: %w", err) + } + + sharedKey, err := dk.Decapsulate(ct) + if err != nil { + return nil, fmt.Errorf("decapsulating ML-KEM 1024 ciphertext: %w", err) + } + + return [][]byte{sharedKey}, nil + }, + } +} + +func lookupCurve(name string) (elliptic.Curve, error) { + var c elliptic.Curve + + switch name { + case "P-224": + c = elliptic.P224() + case "P-256": + c = elliptic.P256() + case "P-384": + c = elliptic.P384() + case "P-521": + c = elliptic.P521() + default: + return nil, fmt.Errorf("unknown curve name: %q", name) + } + + return c, nil +} + +func cmdAesCbc(direction aesDirection) command { + return command{ + requiredArgs: 4, // Key, ciphertext or plaintext, IV, num iterations + handler: func(args [][]byte) ([][]byte, error) { + if direction != aesEncrypt && direction != aesDecrypt { + panic("invalid AES direction") + } + + key := args[0] + input := args[1] + iv := args[2] + numIterations := binary.LittleEndian.Uint32(args[3]) + + blockCipher, err := aes.New(key) + if err != nil { + return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err) + } + + if len(input)%blockCipher.BlockSize() != 0 || len(input) == 0 { + return nil, fmt.Errorf("invalid ciphertext/plaintext size %d: not a multiple of block size %d", + len(input), blockCipher.BlockSize()) + } + + if blockCipher.BlockSize() != len(iv) { + return nil, fmt.Errorf("invalid IV size: expected %d, got %d", blockCipher.BlockSize(), len(iv)) + } + + result := make([]byte, len(input)) + prevResult := make([]byte, len(input)) + prevInput := make([]byte, len(input)) + + for i := uint32(0); i < numIterations; i++ { + copy(prevResult, result) + + if i > 0 { + if direction == aesEncrypt { + copy(iv, result) + } else { + copy(iv, prevInput) + } + } + + if direction == aesEncrypt { + cbcEnc := aes.NewCBCEncrypter(blockCipher, [16]byte(iv)) + cbcEnc.CryptBlocks(result, input) + } else { + cbcDec := aes.NewCBCDecrypter(blockCipher, [16]byte(iv)) + cbcDec.CryptBlocks(result, input) + } + + if direction == aesDecrypt { + copy(prevInput, input) + } + + if i == 0 { + copy(input, iv) + } else { + copy(input, prevResult) + } + } + + return [][]byte{result, prevResult}, nil + }, + } +} + +func cmdAesCtr(direction aesDirection) command { + return command{ + requiredArgs: 4, // Key, ciphertext or plaintext, initial counter, num iterations (constant 1) + handler: func(args [][]byte) ([][]byte, error) { + if direction != aesEncrypt && direction != aesDecrypt { + panic("invalid AES direction") + } + + key := args[0] + input := args[1] + iv := args[2] + numIterations := binary.LittleEndian.Uint32(args[3]) + if numIterations != 1 { + return nil, fmt.Errorf("invalid num iterations: expected 1, got %d", numIterations) + } + + if len(iv) != aes.BlockSize { + return nil, fmt.Errorf("invalid IV size: expected %d, got %d", aes.BlockSize, len(iv)) + } + + blockCipher, err := aes.New(key) + if err != nil { + return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err) + } + + result := make([]byte, len(input)) + stream := aes.NewCTR(blockCipher, iv) + stream.XORKeyStream(result, input) + + return [][]byte{result}, nil + }, + } +} + +func cmdAesGcmSeal(randNonce bool) command { + return command{ + requiredArgs: 5, // tag len, key, plaintext, nonce (empty for randNonce), additional data + handler: func(args [][]byte) ([][]byte, error) { + tagLen := binary.LittleEndian.Uint32(args[0]) + key := args[1] + plaintext := args[2] + nonce := args[3] + additionalData := args[4] + + blockCipher, err := aes.New(key) + if err != nil { + return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err) + } + + aesGCM, err := gcm.New(blockCipher, 12, int(tagLen)) + if err != nil { + return nil, fmt.Errorf("creating AES-GCM with tag len %d: %w", tagLen, err) + } + + var ct []byte + if !randNonce { + ct = aesGCM.Seal(nil, nonce, plaintext, additionalData) + } else { + var internalNonce [12]byte + ct = make([]byte, len(plaintext)+16) + gcm.SealWithRandomNonce(aesGCM, internalNonce[:], ct, plaintext, additionalData) + // acvptool expects the internally generated nonce to be appended to the end of the ciphertext. + ct = append(ct, internalNonce[:]...) + } + + return [][]byte{ct}, nil + }, + } +} + +func cmdAesGcmOpen(randNonce bool) command { + return command{ + requiredArgs: 5, // tag len, key, ciphertext, nonce (empty for randNonce), additional data + handler: func(args [][]byte) ([][]byte, error) { + + tagLen := binary.LittleEndian.Uint32(args[0]) + key := args[1] + ciphertext := args[2] + nonce := args[3] + additionalData := args[4] + + blockCipher, err := aes.New(key) + if err != nil { + return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err) + } + + aesGCM, err := gcm.New(blockCipher, 12, int(tagLen)) + if err != nil { + return nil, fmt.Errorf("creating AES-GCM with tag len %d: %w", tagLen, err) + } + + if randNonce { + // for randNonce tests acvptool appends the nonce to the end of the ciphertext. + nonce = ciphertext[len(ciphertext)-12:] + ciphertext = ciphertext[:len(ciphertext)-12] + } + + pt, err := aesGCM.Open(nil, nonce, ciphertext, additionalData) + if err != nil { + return [][]byte{{0}, nil}, nil + } + + return [][]byte{{1}, pt}, nil + }, + } +} + +func cmdCmacAesAft() command { + return command{ + requiredArgs: 3, // Number of output bytes, key, message + handler: func(args [][]byte) ([][]byte, error) { + // safe to truncate to int based on our capabilities describing a max MAC output len of 128 bits. + outputLen := int(binary.LittleEndian.Uint32(args[0])) + key := args[1] + message := args[2] + + blockCipher, err := aes.New(key) + if err != nil { + return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err) + } + + cmac := gcm.NewCMAC(blockCipher) + tag := cmac.MAC(message) + + if outputLen > len(tag) { + return nil, fmt.Errorf("invalid output length: expected %d, got %d", outputLen, len(tag)) + } + + return [][]byte{tag[:outputLen]}, nil + }, + } +} + +func cmdCmacAesVerifyAft() command { + return command{ + requiredArgs: 3, // Key, message, claimed MAC + handler: func(args [][]byte) ([][]byte, error) { + key := args[0] + message := args[1] + claimedMAC := args[2] + + blockCipher, err := aes.New(key) + if err != nil { + return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err) + } + + cmac := gcm.NewCMAC(blockCipher) + tag := cmac.MAC(message) + + if subtle.ConstantTimeCompare(tag[:len(claimedMAC)], claimedMAC) != 1 { + return [][]byte{{0}}, nil + } + + return [][]byte{{1}}, nil + }, + } +} + +func cmdTlsKdf12Aft(h func() fips140.Hash) command { + return command{ + requiredArgs: 5, // Number output bytes, secret, label, seed1, seed2 + handler: func(args [][]byte) ([][]byte, error) { + outputLen := binary.LittleEndian.Uint32(args[0]) + secret := args[1] + label := string(args[2]) + seed1 := args[3] + seed2 := args[4] + + return [][]byte{tls12.PRF(h, secret, label, append(seed1, seed2...), int(outputLen))}, nil + }, + } +} + +func cmdSshKdfAft(hFunc func() fips140.Hash, direction ssh.Direction) command { + return command{ + requiredArgs: 4, // K, H, SessionID, cipher + handler: func(args [][]byte) ([][]byte, error) { + k := args[0] + h := args[1] + sessionID := args[2] + cipher := string(args[3]) + + var keyLen int + switch cipher { + case "AES-128": + keyLen = 16 + case "AES-192": + keyLen = 24 + case "AES-256": + keyLen = 32 + default: + return nil, fmt.Errorf("unsupported cipher: %q", cipher) + } + + ivKey, encKey, intKey := ssh.Keys(hFunc, direction, k, h, sessionID, 16, keyLen, hFunc().Size()) + return [][]byte{ivKey, encKey, intKey}, nil + }, + } +} + +func cmdEcdhAftVal[P ecdh.Point[P]](curve *ecdh.Curve[P]) command { + return command{ + requiredArgs: 3, // X, Y, private key (empty for Val type tests) + handler: func(args [][]byte) ([][]byte, error) { + peerX := args[0] + peerY := args[1] + rawSk := args[2] + + uncompressedPk := append([]byte{4}, append(peerX, peerY...)...) // 4 for uncompressed point format + pk, err := ecdh.NewPublicKey(curve, uncompressedPk) + if err != nil { + return nil, fmt.Errorf("invalid peer public key x,y: %v", err) + } + + var sk *ecdh.PrivateKey + if len(rawSk) > 0 { + sk, err = ecdh.NewPrivateKey(curve, rawSk) + } else { + sk, err = ecdh.GenerateKey(curve, rand.Reader) + } + if err != nil { + return nil, fmt.Errorf("private key error: %v", err) + } + + pubBytes := sk.PublicKey().Bytes() + coordLen := (len(pubBytes) - 1) / 2 + x := pubBytes[1 : 1+coordLen] + y := pubBytes[1+coordLen:] + + secret, err := ecdh.ECDH(curve, sk, pk) + if err != nil { + return nil, fmt.Errorf("key agreement failed: %v", err) + } + + return [][]byte{x, y, secret}, nil + }, + } +} + +func cmdHmacDrbgAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 6, // Output length, entropy, personalization, ad1, ad2, nonce + handler: func(args [][]byte) ([][]byte, error) { + outLen := binary.LittleEndian.Uint32(args[0]) + entropy := args[1] + personalization := args[2] + ad1 := args[3] + ad2 := args[4] + nonce := args[5] + + // Our capabilities describe no additional data support. + if len(ad1) != 0 || len(ad2) != 0 { + return nil, errors.New("additional data not supported") + } + + // Our capabilities describe no prediction resistance (requires reseed) and no reseed. + // So the test procedure is: + // * Instantiate DRBG + // * Generate but don't output + // * Generate output + // * Uninstantiate + // See Table 7 in draft-vassilev-acvp-drbg + out := make([]byte, outLen) + drbg := ecdsa.TestingOnlyNewDRBG(h, entropy, nonce, personalization) + drbg.Generate(out) + drbg.Generate(out) + + return [][]byte{out}, nil + }, + } +} + +func cmdCtrDrbgAft() command { + return command{ + requiredArgs: 6, // Output length, entropy, personalization, ad1, ad2, nonce + handler: func(args [][]byte) ([][]byte, error) { + return acvpCtrDrbg{ + outLen: binary.LittleEndian.Uint32(args[0]), + entropy: args[1], + personalization: args[2], + ad1: args[3], + ad2: args[4], + nonce: args[5], + }.process() + }, + } +} + +func cmdCtrDrbgReseedAft() command { + return command{ + requiredArgs: 8, // Output length, entropy, personalization, reseedAD, reseedEntropy, ad1, ad2, nonce + handler: func(args [][]byte) ([][]byte, error) { + return acvpCtrDrbg{ + outLen: binary.LittleEndian.Uint32(args[0]), + entropy: args[1], + personalization: args[2], + reseedAd: args[3], + reseedEntropy: args[4], + ad1: args[5], + ad2: args[6], + nonce: args[7], + }.process() + }, + } +} + +type acvpCtrDrbg struct { + outLen uint32 + entropy []byte + personalization []byte + ad1 []byte + ad2 []byte + nonce []byte + reseedAd []byte // May be empty for no reseed + reseedEntropy []byte // May be empty for no reseed +} + +func (args acvpCtrDrbg) process() ([][]byte, error) { + // Our capability describes no personalization support. + if len(args.personalization) > 0 { + return nil, errors.New("personalization string not supported") + } + + // Our capability describes no derivation function support, so the nonce + // should be empty. + if len(args.nonce) > 0 { + return nil, errors.New("unexpected nonce value") + } + + // Our capability describes entropy input len of 384 bits. + entropy, err := require48Bytes(args.entropy) + if err != nil { + return nil, fmt.Errorf("entropy: %w", err) + } + + // Our capability describes additional input len of 384 bits. + ad1, err := require48Bytes(args.ad1) + if err != nil { + return nil, fmt.Errorf("AD1: %w", err) + } + ad2, err := require48Bytes(args.ad2) + if err != nil { + return nil, fmt.Errorf("AD2: %w", err) + } + + withReseed := len(args.reseedAd) > 0 + var reseedAd, reseedEntropy *[48]byte + if withReseed { + // Ditto RE: entropy and additional data lengths for reseeding. + if reseedAd, err = require48Bytes(args.reseedAd); err != nil { + return nil, fmt.Errorf("reseed AD: %w", err) + } + if reseedEntropy, err = require48Bytes(args.reseedEntropy); err != nil { + return nil, fmt.Errorf("reseed entropy: %w", err) + } + } + + // Our capabilities describe no prediction resistance and allow both + // reseed and no reseed, so the test procedure is: + // * Instantiate DRBG + // * Reseed (if enabled) + // * Generate but don't output + // * Generate output + // * Uninstantiate + // See Table 7 in draft-vassilev-acvp-drbg + out := make([]byte, args.outLen) + ctrDrbg := drbg.NewCounter(entropy) + if withReseed { + ctrDrbg.Reseed(reseedEntropy, reseedAd) + } + ctrDrbg.Generate(out, ad1) + ctrDrbg.Generate(out, ad2) + + return [][]byte{out}, nil +} + +// Verify input is 48 byte slice, and cast it to a pointer to a fixed-size array +// of 48 bytes, or return an error. +func require48Bytes(input []byte) (*[48]byte, error) { + if inputLen := len(input); inputLen != 48 { + return nil, fmt.Errorf("invalid length: %d", inputLen) + } + return (*[48]byte)(input), nil +} + +func cmdKdfCounterAft() command { + return command{ + requiredArgs: 5, // Number output bytes, PRF name, counter location string, key, number of counter bits + handler: func(args [][]byte) ([][]byte, error) { + outputBytes := binary.LittleEndian.Uint32(args[0]) + prf := args[1] + counterLocation := args[2] + key := args[3] + counterBits := binary.LittleEndian.Uint32(args[4]) + + if outputBytes != 32 { + return nil, fmt.Errorf("KDF received unsupported output length %d bytes", outputBytes) + } + if !bytes.Equal(prf, []byte("CMAC-AES128")) && !bytes.Equal(prf, []byte("CMAC-AES192")) && !bytes.Equal(prf, []byte("CMAC-AES256")) { + return nil, fmt.Errorf("KDF received unsupported PRF %q", string(prf)) + } + if !bytes.Equal(counterLocation, []byte("before fixed data")) { + return nil, fmt.Errorf("KDF received unsupported counter location %q", string(counterLocation)) + } + // The spec doesn't describe the "deferred" property for a KDF counterMode test case. + // BoringSSL's acvptool sends an empty key when deferred=true, but with the capabilities + // we register all test cases ahve deferred=false and provide a key from the populated + // keyIn property. + if len(key) == 0 { + return nil, errors.New("deferred test cases are not supported") + } + if counterBits != 16 { + return nil, fmt.Errorf("KDF received unsupported counter length %d", counterBits) + } + + block, err := aes.New(key) + if err != nil { + return nil, fmt.Errorf("failed to create cipher: %v", err) + } + kdf := gcm.NewCounterKDF(block) + + var label byte + var context [12]byte + rand.Reader.Read(context[:]) + + result := kdf.DeriveKey(label, context) + + fixedData := make([]byte, 1+1+12) // 1 byte label, 1 null byte, 12 bytes context. + fixedData[0] = label + copy(fixedData[2:], context[:]) + + return [][]byte{key, fixedData, result[:]}, nil + }, + } +} + +func cmdKdfFeedbackAft() command { + return command{ + requiredArgs: 5, // Number output bytes, PRF name, counter location string, key, number of counter bits, IV + handler: func(args [][]byte) ([][]byte, error) { + // The max supported output len for the KDF algorithm type is 4096 bits, making an int cast + // here safe. + // See https://pages.nist.gov/ACVP/draft-celi-acvp-kbkdf.html#section-7.3.2 + outputBytes := int(binary.LittleEndian.Uint32(args[0])) + prf := string(args[1]) + counterLocation := args[2] + key := args[3] + counterBits := binary.LittleEndian.Uint32(args[4]) + + if !strings.HasPrefix(prf, "HMAC-") { + return nil, fmt.Errorf("feedback KDF received unsupported PRF %q", prf) + } + prf = prf[len("HMAC-"):] + + h, err := lookupHash(prf) + if err != nil { + return nil, fmt.Errorf("feedback KDF received unsupported PRF %q: %w", prf, err) + } + + if !bytes.Equal(counterLocation, []byte("after fixed data")) { + return nil, fmt.Errorf("feedback KDF received unsupported counter location %q", string(counterLocation)) + } + + // The spec doesn't describe the "deferred" property for a KDF counterMode test case. + // BoringSSL's acvptool sends an empty key when deferred=true, but with the capabilities + // we register all test cases have deferred=false and provide a key from the populated + // keyIn property. + if len(key) == 0 { + return nil, errors.New("deferred test cases are not supported") + } + + if counterBits != 8 { + return nil, fmt.Errorf("feedback KDF received unsupported counter length %d", counterBits) + } + + var context [12]byte + rand.Reader.Read(context[:]) + fixedData := make([]byte, 1+1+12) // 1 byte label (we pick null), 1 null byte, 12 bytes context. + copy(fixedData[2:], context[:]) + + result := hkdf.Expand(h, key, string(fixedData[:]), outputBytes) + + return [][]byte{key, fixedData[:], result[:]}, nil + }, + } +} + +func cmdRsaKeyGenAft() command { + return command{ + requiredArgs: 1, // Modulus bit-size + handler: func(args [][]byte) ([][]byte, error) { + bitSize := binary.LittleEndian.Uint32(args[0]) + + key, err := getRSAKey((int)(bitSize)) + if err != nil { + return nil, fmt.Errorf("generating RSA key: %w", err) + } + + N, e, d, P, Q, _, _, _ := key.Export() + + eBytes := make([]byte, 4) + binary.BigEndian.PutUint32(eBytes, uint32(e)) + + return [][]byte{eBytes, P, Q, N, d}, nil + }, + } +} + +func cmdRsaSigGenAft(hashFunc func() fips140.Hash, hashName string, pss bool) command { + return command{ + requiredArgs: 2, // Modulus bit-size, message + handler: func(args [][]byte) ([][]byte, error) { + bitSize := binary.LittleEndian.Uint32(args[0]) + msg := args[1] + + key, err := getRSAKey((int)(bitSize)) + if err != nil { + return nil, fmt.Errorf("generating RSA key: %w", err) + } + + h := hashFunc() + h.Write(msg) + digest := h.Sum(nil) + + var sig []byte + if !pss { + sig, err = rsa.SignPKCS1v15(key, hashName, digest) + if err != nil { + return nil, fmt.Errorf("signing RSA message: %w", err) + } + } else { + sig, err = rsa.SignPSS(rand.Reader, key, hashFunc(), digest, h.Size()) + if err != nil { + return nil, fmt.Errorf("signing RSA message: %w", err) + } + } + + N, e, _, _, _, _, _, _ := key.Export() + eBytes := make([]byte, 4) + binary.BigEndian.PutUint32(eBytes, uint32(e)) + + return [][]byte{N, eBytes, sig}, nil + }, + } +} + +func cmdRsaSigVerAft(hashFunc func() fips140.Hash, hashName string, pss bool) command { + return command{ + requiredArgs: 4, // n, e, message, signature + handler: func(args [][]byte) ([][]byte, error) { + nBytes := args[0] + eBytes := args[1] + msg := args[2] + sig := args[3] + + paddedE := make([]byte, 4) + copy(paddedE[4-len(eBytes):], eBytes) + e := int(binary.BigEndian.Uint32(paddedE)) + + n, err := bigmod.NewModulus(nBytes) + if err != nil { + return nil, fmt.Errorf("invalid RSA modulus: %w", err) + } + + pub := &rsa.PublicKey{ + N: n, + E: e, + } + + h := hashFunc() + h.Write(msg) + digest := h.Sum(nil) + + if !pss { + err = rsa.VerifyPKCS1v15(pub, hashName, digest, sig) + } else { + err = rsa.VerifyPSS(pub, hashFunc(), digest, sig) + } + if err != nil { + return [][]byte{{0}}, nil + } + + return [][]byte{{1}}, nil + }, + } +} + +// rsaKeyCache caches generated keys by modulus bit-size. +var rsaKeyCache = map[int]*rsa.PrivateKey{} + +// getRSAKey returns a cached RSA private key with the specified modulus bit-size +// or generates one if necessary. +func getRSAKey(bits int) (*rsa.PrivateKey, error) { + if key, exists := rsaKeyCache[bits]; exists { + return key, nil + } + + key, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return nil, err + } + + rsaKeyCache[bits] = key + return key, nil +} + +func cmdOneStepNoCounterHmacAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 4, // key, info, salt, outBytes + handler: func(args [][]byte) ([][]byte, error) { + key := args[0] + info := args[1] + salt := args[2] + outBytes := binary.LittleEndian.Uint32(args[3]) + + mac := hmac.New(h, salt) + mac.Size() + + if outBytes != uint32(mac.Size()) { + return nil, fmt.Errorf("invalid output length: got %d, want %d", outBytes, mac.Size()) + } + + data := make([]byte, 0, len(key)+len(info)) + data = append(data, key...) + data = append(data, info...) + + mac.Write(data) + out := mac.Sum(nil) + + return [][]byte{out}, nil + }, + } +} + +func cmdKtsIfcInitiatorAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 3, // output bytes, n bytes, e bytes + handler: func(args [][]byte) ([][]byte, error) { + outputBytes := binary.LittleEndian.Uint32(args[0]) + nBytes := args[1] + eBytes := args[2] + + n, err := bigmod.NewModulus(nBytes) + if err != nil { + return nil, fmt.Errorf("invalid RSA modulus: %w", err) + } + + paddedE := make([]byte, 4) + copy(paddedE[4-len(eBytes):], eBytes) + e := int(binary.BigEndian.Uint32(paddedE)) + if e != 0x10001 { + return nil, errors.New("e must be 0x10001") + } + + pub := &rsa.PublicKey{ + N: n, + E: e, + } + + dkm := make([]byte, outputBytes) + if _, err := rand.Read(dkm); err != nil { + return nil, fmt.Errorf("failed to generate random DKM: %v", err) + } + + iutC, err := rsa.EncryptOAEP(h(), h(), rand.Reader, pub, dkm, nil) + if err != nil { + return nil, fmt.Errorf("OAEP encryption failed: %v", err) + } + + return [][]byte{iutC, dkm}, nil + }, + } +} + +func cmdKtsIfcResponderAft(h func() fips140.Hash) command { + return command{ + requiredArgs: 6, // n bytes, e bytes, p bytes, q bytes, d bytes, c bytes + handler: func(args [][]byte) ([][]byte, error) { + nBytes := args[0] + eBytes := args[1] + + pBytes := args[2] + qBytes := args[3] + dBytes := args[4] + + cBytes := args[5] + + paddedE := make([]byte, 4) + copy(paddedE[4-len(eBytes):], eBytes) + e := int(binary.BigEndian.Uint32(paddedE)) + if e != 0x10001 { + return nil, errors.New("e must be 0x10001") + } + + priv, err := rsa.NewPrivateKey(nBytes, int(e), dBytes, pBytes, qBytes) + if err != nil { + return nil, fmt.Errorf("failed to create private key: %v", err) + } + + dkm, err := rsa.DecryptOAEP(h(), h(), priv, cBytes, nil) + if err != nil { + return nil, fmt.Errorf("OAEP decryption failed: %v", err) + } + + return [][]byte{dkm}, nil + }, + } +} + +func TestACVP(t *testing.T) { + testenv.SkipIfShortAndSlow(t) + + const ( + bsslModule = "boringssl.googlesource.com/boringssl.git" + bsslVersion = "v0.0.0-20250207174145-0bb19f6126cb" + goAcvpModule = "github.com/cpu/go-acvp" + goAcvpVersion = "v0.0.0-20250126154732-de1ba727a0be" + ) + + // In crypto/tls/bogo_shim_test.go the test is skipped if run on a builder with runtime.GOOS == "windows" + // due to flaky networking. It may be necessary to do the same here. + + // Stat the acvp test config file so the test will be re-run if it changes, invalidating cached results + // from the old config. + if _, err := os.Stat("acvp_test.config.json"); err != nil { + t.Fatalf("failed to stat config file: %s", err) + } + + // Fetch the BSSL module and use the JSON output to find the absolute path to the dir. + bsslDir := cryptotest.FetchModule(t, bsslModule, bsslVersion) + + t.Log("building acvptool") + + // Build the acvptool binary. + toolPath := filepath.Join(t.TempDir(), "acvptool.exe") + goTool := testenv.GoToolPath(t) + cmd := testenv.Command(t, goTool, + "build", + "-o", toolPath, + "./util/fipstools/acvp/acvptool") cmd.Dir = bsslDir out := &strings.Builder{} cmd.Stderr = out @@ -467,7 +2132,10 @@ func TestACVP(t *testing.T) { } cmd = testenv.Command(t, goTool, args...) cmd.Dir = dataDir - cmd.Env = append(os.Environ(), "ACVP_WRAPPER=1") + cmd.Env = append(os.Environ(), + "ACVP_WRAPPER=1", + "GODEBUG=fips140=on", + ) output, err := cmd.CombinedOutput() if err != nil { t.Fatalf("failed to run acvp tests: %s\n%s", err, string(output)) diff --git a/src/crypto/internal/fips140test/cast_test.go b/src/crypto/internal/fips140test/cast_test.go index c6e3212f3f064f..41122f339d1831 100644 --- a/src/crypto/internal/fips140test/cast_test.go +++ b/src/crypto/internal/fips140test/cast_test.go @@ -13,6 +13,7 @@ import ( "io/fs" "os" "regexp" + "slices" "strings" "testing" @@ -34,7 +35,35 @@ import ( _ "crypto/internal/fips140/tls13" ) -func findAllCASTs(t *testing.T) map[string]struct{} { +var allCASTs = []string{ + "AES-CBC", + "CTR_DRBG", + "CounterKDF", + "DetECDSA P-256 SHA2-512 sign", + "ECDH PCT", + "ECDSA P-256 SHA2-512 sign and verify", + "ECDSA PCT", + "Ed25519 sign and verify", + "Ed25519 sign and verify PCT", + "HKDF-SHA2-256", + "HMAC-SHA2-256", + "KAS-ECC-SSC P-256", + "ML-KEM PCT", + "ML-KEM PCT", + "ML-KEM PCT", + "ML-KEM PCT", + "ML-KEM-768", + "PBKDF2", + "RSA sign and verify PCT", + "RSASSA-PKCS-v1.5 2048-bit sign and verify", + "SHA2-256", + "SHA2-512", + "TLSv1.2-SHA2-256", + "TLSv1.3-SHA2-256", + "cSHAKE128", +} + +func TestAllCASTs(t *testing.T) { testenv.MustHaveSource(t) // Ask "go list" for the location of the crypto/internal/fips140 tree, as it @@ -48,7 +77,7 @@ func findAllCASTs(t *testing.T) map[string]struct{} { t.Logf("FIPS module directory: %s", fipsDir) // Find all invocations of fips140.CAST or fips140.PCT. - allCASTs := make(map[string]struct{}) + var foundCASTs []string castRe := regexp.MustCompile(`fips140\.(CAST|PCT)\("([^"]+)"`) if err := fs.WalkDir(os.DirFS(fipsDir), ".", func(path string, d fs.DirEntry, err error) error { if err != nil { @@ -62,14 +91,17 @@ func findAllCASTs(t *testing.T) map[string]struct{} { return err } for _, m := range castRe.FindAllSubmatch(data, -1) { - allCASTs[string(m[2])] = struct{}{} + foundCASTs = append(foundCASTs, string(m[2])) } return nil }); err != nil { t.Fatalf("WalkDir: %v", err) } - return allCASTs + slices.Sort(foundCASTs) + if !slices.Equal(foundCASTs, allCASTs) { + t.Errorf("AllCASTs is out of date. Found CASTs: %#v", foundCASTs) + } } // TestConditionals causes the conditional CASTs and PCTs to be invoked. @@ -85,7 +117,7 @@ func TestConditionals(t *testing.T) { t.Fatal(err) } ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, kDSA, make([]byte, 32)) - k25519, err := ed25519.GenerateKey(rand.Reader) + k25519, err := ed25519.GenerateKey() if err != nil { t.Fatal(err) } @@ -127,28 +159,29 @@ UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0 } func TestCASTFailures(t *testing.T) { + moduleStatus(t) testenv.MustHaveExec(t) - allCASTs := findAllCASTs(t) - if len(allCASTs) == 0 { - t.Fatal("no CASTs found") - } - - for name := range allCASTs { + for _, name := range allCASTs { t.Run(name, func(t *testing.T) { - t.Parallel() + // Don't parallelize if running in verbose mode, to produce a less + // confusing recoding for the validation lab. + if !testing.Verbose() { + t.Parallel() + } + t.Logf("CAST/PCT succeeded: %s", name) + t.Logf("Testing CAST/PCT failure...") cmd := testenv.Command(t, testenv.Executable(t), "-test.run=TestConditionals", "-test.v") - cmd = testenv.CleanCmdEnv(cmd) cmd.Env = append(cmd.Env, fmt.Sprintf("GODEBUG=failfipscast=%s,fips140=on", name)) out, err := cmd.CombinedOutput() + t.Logf("%s", out) if err == nil { - t.Error(err) - } else { - t.Logf("CAST/PCT %s failed and caused the program to exit or the test to fail", name) - t.Logf("%s", out) + t.Fatal("Test did not fail as expected") } if strings.Contains(string(out), "completed successfully") { t.Errorf("CAST/PCT %s failure did not stop the program", name) + } else { + t.Logf("CAST/PCT %s failed as expected and caused the program to exit", name) } }) } diff --git a/src/crypto/internal/fips140test/check_test.go b/src/crypto/internal/fips140test/check_test.go index b156de2cbbab6c..c014fff2a6d80d 100644 --- a/src/crypto/internal/fips140test/check_test.go +++ b/src/crypto/internal/fips140test/check_test.go @@ -5,45 +5,37 @@ package fipstest import ( + "bytes" + "crypto/internal/fips140" . "crypto/internal/fips140/check" "crypto/internal/fips140/check/checktest" "fmt" "internal/abi" - "internal/asan" "internal/godebug" "internal/testenv" "os" + "path/filepath" "runtime" "testing" "unicode" "unsafe" ) -const enableFIPSTest = true - -func TestFIPSCheckVerify(t *testing.T) { +func TestIntegrityCheck(t *testing.T) { if Verified { t.Logf("verified") return } - if godebug.New("#fips140").Value() == "on" { + if godebug.New("fips140").Value() == "on" { t.Fatalf("GODEBUG=fips140=on but verification did not run") } - if !enableFIPSTest { - return - } - - if !Supported() { - t.Skipf("skipping on %s-%s", runtime.GOOS, runtime.GOARCH) - } - if asan.Enabled { - // Verification panics with asan; don't bother. - t.Skipf("skipping with -asan") + if err := fips140.Supported(); err != nil { + t.Skipf("skipping: %v", err) } - cmd := testenv.Command(t, os.Args[0], "-test.v", "-test.run=TestFIPSCheck") + cmd := testenv.Command(t, os.Args[0], "-test.v", "-test.run=TestIntegrityCheck") cmd.Env = append(cmd.Environ(), "GODEBUG=fips140=on") out, err := cmd.CombinedOutput() if err != nil { @@ -52,13 +44,54 @@ func TestFIPSCheckVerify(t *testing.T) { t.Logf("exec'ed GODEBUG=fips140=on and succeeded:\n%s", out) } -func TestFIPSCheckInfo(t *testing.T) { - if !enableFIPSTest { - return +func TestIntegrityCheckFailure(t *testing.T) { + moduleStatus(t) + testenv.MustHaveExec(t) + if err := fips140.Supported(); err != nil { + t.Skipf("skipping: %v", err) + } + + bin, err := os.ReadFile(os.Args[0]) + if err != nil { + t.Fatal(err) + } + + // Replace the expected module checksum with a different value. + bin = bytes.ReplaceAll(bin, Linkinfo.Sum[:], bytes.Repeat([]byte("X"), len(Linkinfo.Sum))) + + binPath := filepath.Join(t.TempDir(), "fips140test.exe") + if err := os.WriteFile(binPath, bin, 0o755); err != nil { + t.Fatal(err) + } + + if runtime.GOOS == "darwin" { + // Regenerate the macOS ad-hoc code signature. + cmd := testenv.Command(t, "codesign", "-s", "-", "-f", binPath) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("codesign failed: %v\n%s", err, out) + } } - if !Supported() { - t.Skipf("skipping on %s-%s", runtime.GOOS, runtime.GOARCH) + t.Logf("running modified binary...") + cmd := testenv.Command(t, binPath, "-test.v", "-test.run=TestIntegrityCheck$") + cmd.Env = append(cmd.Environ(), "GODEBUG=fips140=on") + out, err := cmd.CombinedOutput() + t.Logf("%s", out) + if err == nil { + t.Errorf("modified binary did not fail as expected") + } + if !bytes.Contains(out, []byte("fips140: verification mismatch")) { + t.Errorf("modified binary did not fail with expected message") + } + if bytes.Contains(out, []byte("verified")) { + t.Errorf("modified binary did not exit") + } +} + +func TestIntegrityCheckInfo(t *testing.T) { + if err := fips140.Supported(); err != nil { + t.Skipf("skipping: %v", err) } // Check that the checktest symbols are initialized properly. diff --git a/src/crypto/internal/fips140test/fips_test.go b/src/crypto/internal/fips140test/fips_test.go index 8da5278050ba6c..81ccd0cf7fdd1d 100644 --- a/src/crypto/internal/fips140test/fips_test.go +++ b/src/crypto/internal/fips140test/fips_test.go @@ -9,14 +9,427 @@ // to either minimize, skip, or remove them. Finally, the module needs to avoid // importing internal packages like testenv and cryptotest to avoid locking in // their APIs. +// +// Also, this package includes the ACVP and functional testing harnesses. package fipstest import ( + "bytes" + "crypto/internal/boring" + "crypto/internal/fips140" + "crypto/internal/fips140/aes" + "crypto/internal/fips140/aes/gcm" + "crypto/internal/fips140/check" + "crypto/internal/fips140/drbg" + "crypto/internal/fips140/ecdh" + "crypto/internal/fips140/ecdsa" + "crypto/internal/fips140/ed25519" + "crypto/internal/fips140/hkdf" + "crypto/internal/fips140/hmac" + "crypto/internal/fips140/mlkem" + "crypto/internal/fips140/pbkdf2" + "crypto/internal/fips140/rsa" + "crypto/internal/fips140/sha256" + "crypto/internal/fips140/sha3" + "crypto/internal/fips140/sha512" + "crypto/internal/fips140/tls12" + "crypto/internal/fips140/tls13" + "crypto/rand" "encoding/hex" "strings" "testing" ) +func moduleStatus(t *testing.T) { + if fips140.Enabled { + t.Log("FIPS 140-3 mode enabled") + } else { + t.Log("FIPS 140-3 mode not enabled") + } + + t.Logf("Module name: %s", fips140.Name()) + t.Logf("Module version: %s", fips140.Version()) + + if noPAAPAI { + t.Log("PAA/PAI disabled") + } else { + t.Log("PAA/PAI enabled") + } + + if check.Verified { + t.Log("FIPS 140-3 integrity self-check succeeded") + } else { + t.Log("FIPS 140-3 integrity self-check not succeeded") + } +} + +func TestFIPS140(t *testing.T) { + moduleStatus(t) + if boring.Enabled { + t.Skip("Go+BoringCrypto shims prevent the service indicator from being set") + } + + aesKey := make([]byte, 128/8) + aesIV := make([]byte, aes.BlockSize) + plaintext := []byte("Go Cryptographic Module TestFIPS140 plaintext...") + plaintextSHA256 := decodeHex(t, "06b2614e2ef315832b23f5d0ff70294d8ddd3889527dfbe75707fe41da929325") + aesBlock, err := aes.New(aesKey) + fatalIfErr(t, err) + + t.Run("AES-CTR", func(t *testing.T) { + ensureServiceIndicator(t) + ctr := aes.NewCTR(aesBlock, aesIV) + ciphertext := make([]byte, len(plaintext)) + ctr.XORKeyStream(ciphertext, plaintext) + t.Logf("AES-CTR ciphertext: %x", ciphertext) + out := make([]byte, len(plaintext)) + ctr = aes.NewCTR(aesBlock, aesIV) + ctr.XORKeyStream(out, ciphertext) + t.Logf("AES-CTR decrypted plaintext: %s", out) + if !bytes.Equal(plaintext, out) { + t.Errorf("AES-CTR round trip failed") + } + }) + + t.Run("AES-CBC", func(t *testing.T) { + ensureServiceIndicator(t) + cbcEnc := aes.NewCBCEncrypter(aesBlock, [16]byte(aesIV)) + ciphertext := make([]byte, len(plaintext)) + cbcEnc.CryptBlocks(ciphertext, plaintext) + t.Logf("AES-CBC ciphertext: %x", ciphertext) + cbcDec := aes.NewCBCDecrypter(aesBlock, [16]byte(aesIV)) + out := make([]byte, len(plaintext)) + cbcDec.CryptBlocks(out, ciphertext) + t.Logf("AES-CBC decrypted plaintext: %s", out) + if !bytes.Equal(plaintext, out) { + t.Errorf("AES-CBC round trip failed") + } + }) + + t.Run("AES-GCM", func(t *testing.T) { + ensureServiceIndicator(t) + g, err := gcm.New(aesBlock, 12, 16) + fatalIfErr(t, err) + nonce := make([]byte, 12) + ciphertext := make([]byte, len(plaintext)+g.Overhead()) + gcm.SealWithRandomNonce(g, nonce, ciphertext, plaintext, nil) + t.Logf("AES-GCM ciphertext: %x", ciphertext) + out, err := g.Open(nil, nonce, ciphertext, nil) + fatalIfErr(t, err) + t.Logf("AES-GCM decrypted plaintext: %s", out) + if !bytes.Equal(plaintext, out) { + t.Errorf("AES-GCM round trip failed") + } + }) + + t.Run("Counter KDF", func(t *testing.T) { + ensureServiceIndicator(t) + k := gcm.NewCounterKDF(aesBlock) + context := [12]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} + key := k.DeriveKey(0x01, context) + t.Logf("Counter KDF key: %x", key) + }) + + t.Run("KAS-ECC-SSC ephemeralUnified", func(t *testing.T) { + ensureServiceIndicator(t) + k, err := ecdh.GenerateKey(ecdh.P256(), rand.Reader) + fatalIfErr(t, err) + pk := k.PublicKey() + shared, err := ecdh.ECDH(ecdh.P256(), k, pk) + fatalIfErr(t, err) + t.Logf("KAS-ECC-SSC shared secret: %x", shared) + }) + + t.Run("ECDSA KeyGen, SigGen, SigVer", func(t *testing.T) { + ensureServiceIndicator(t) + k, err := ecdsa.GenerateKey(ecdsa.P256(), rand.Reader) + fatalIfErr(t, err) + + sig, err := ecdsa.Sign(ecdsa.P256(), sha256.New, k, rand.Reader, plaintextSHA256) + fatalIfErr(t, err) + t.Logf("ECDSA signature: %x", sig) + err = ecdsa.Verify(ecdsa.P256(), k.PublicKey(), plaintextSHA256, sig) + if err != nil { + t.Errorf("ECDSA signature verification failed") + } + + sig, err = ecdsa.SignDeterministic(ecdsa.P256(), sha256.New, k, plaintextSHA256) + fatalIfErr(t, err) + t.Logf("ECDSA deterministic signature: %x", sig) + err = ecdsa.Verify(ecdsa.P256(), k.PublicKey(), plaintextSHA256, sig) + if err != nil { + t.Errorf("ECDSA deterministic signature verification failed") + } + }) + + t.Run("EDDSA KeyGen, SigGen, SigVer", func(t *testing.T) { + ensureServiceIndicator(t) + k, err := ed25519.GenerateKey() + fatalIfErr(t, err) + + sig := ed25519.Sign(k, plaintext) + t.Logf("EDDSA signature: %x", sig) + + pk, err := ed25519.NewPublicKey(k.PublicKey()) + fatalIfErr(t, err) + err = ed25519.Verify(pk, plaintext, sig) + if err != nil { + t.Errorf("EDDSA signature verification failed") + } + }) + + t.Run("ctrDRBG", func(t *testing.T) { + ensureServiceIndicator(t) + r := drbg.NewCounter((*[48]byte)(plaintext)) + r.Reseed((*[48]byte)(plaintext), (*[48]byte)(plaintext)) + out := make([]byte, 16) + r.Generate(out, (*[48]byte)(plaintext)) + t.Logf("ctrDRBG output: %x", out) + }) + + t.Run("HMAC", func(t *testing.T) { + ensureServiceIndicator(t) + h := hmac.New(sha256.New, plaintext) + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("HMAC output: %x", out) + }) + + t.Run("ML-KEM KeyGen, Encap, Decap", func(t *testing.T) { + ensureServiceIndicator(t) + k, err := mlkem.GenerateKey768() + fatalIfErr(t, err) + + ss, c := k.EncapsulationKey().Encapsulate() + t.Logf("ML-KEM encapsulation: %x", c) + + ss2, err := k.Decapsulate(c) + fatalIfErr(t, err) + t.Logf("ML-KEM shared secret: %x", ss) + if !bytes.Equal(ss, ss2) { + t.Errorf("ML-KEM round trip failed") + } + }) + + var rsaKey *rsa.PrivateKey + t.Run("RSA KeyGen", func(t *testing.T) { + ensureServiceIndicator(t) + var err error + rsaKey, err = rsa.GenerateKey(rand.Reader, 2048) + fatalIfErr(t, err) + t.Log("RSA key generated") + }) + + t.Run("RSA SigGen, SigVer PKCS 1.5", func(t *testing.T) { + ensureServiceIndicator(t) + sig, err := rsa.SignPKCS1v15(rsaKey, "SHA-256", plaintextSHA256) + fatalIfErr(t, err) + t.Logf("RSA PKCS1v15 signature: %x", sig) + + err = rsa.VerifyPKCS1v15(rsaKey.PublicKey(), "SHA-256", plaintextSHA256, sig) + fatalIfErr(t, err) + }) + + t.Run("RSA SigGen, SigVer PSS", func(t *testing.T) { + ensureServiceIndicator(t) + sig, err := rsa.SignPSS(rand.Reader, rsaKey, sha256.New(), plaintextSHA256, 16) + fatalIfErr(t, err) + t.Logf("RSA PSS signature: %x", sig) + + err = rsa.VerifyPSS(rsaKey.PublicKey(), sha256.New(), plaintextSHA256, sig) + fatalIfErr(t, err) + }) + + t.Run("KTS IFC OAEP", func(t *testing.T) { + ensureServiceIndicator(t) + c, err := rsa.EncryptOAEP(sha256.New(), sha256.New(), rand.Reader, rsaKey.PublicKey(), plaintextSHA256, nil) + fatalIfErr(t, err) + t.Logf("RSA OAEP ciphertext: %x", c) + + out, err := rsa.DecryptOAEP(sha256.New(), sha256.New(), rsaKey, c, nil) + fatalIfErr(t, err) + t.Logf("RSA OAEP decrypted plaintext: %x", out) + if !bytes.Equal(plaintextSHA256, out) { + t.Errorf("RSA OAEP round trip failed") + } + }) + + t.Run("SHA2-224", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha256.New224() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA2-224 output: %x", out) + }) + + t.Run("SHA2-256", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha256.New() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA2-256 output: %x", out) + }) + + t.Run("SHA2-384", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha512.New384() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA2-384 output: %x", out) + }) + + t.Run("SHA2-512", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha512.New() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA2-512 output: %x", out) + }) + + t.Run("SHA2-512/224", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha512.New512_224() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA2-512/224 output: %x", out) + }) + + t.Run("SHA2-512/256", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha512.New512_256() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA2-512/256 output: %x", out) + }) + + t.Run("SHA3-224", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.New224() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA3-224 output: %x", out) + }) + + t.Run("SHA3-256", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.New256() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA3-256 output: %x", out) + }) + + t.Run("SHA3-384", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.New384() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA3-384 output: %x", out) + }) + + t.Run("SHA3-512", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.New512() + h.Write(plaintext) + out := h.Sum(nil) + t.Logf("SHA3-512 output: %x", out) + }) + + t.Run("SHAKE-128", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.NewShake128() + h.Write(plaintext) + out := make([]byte, 16) + h.Read(out) + t.Logf("SHAKE-128 output: %x", out) + }) + + t.Run("SHAKE-256", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.NewShake256() + h.Write(plaintext) + out := make([]byte, 16) + h.Read(out) + t.Logf("SHAKE-256 output: %x", out) + }) + + t.Run("cSHAKE-128", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.NewCShake128(nil, []byte("test")) + h.Write(plaintext) + out := make([]byte, 16) + h.Read(out) + t.Logf("cSHAKE-128 output: %x", out) + }) + + t.Run("cSHAKE-256", func(t *testing.T) { + ensureServiceIndicator(t) + h := sha3.NewCShake256(nil, []byte("test")) + h.Write(plaintext) + out := make([]byte, 16) + h.Read(out) + t.Logf("cSHAKE-256 output: %x", out) + }) + + t.Run("KDA HKDF", func(t *testing.T) { + ensureServiceIndicator(t) + key := hkdf.Key(sha256.New, plaintextSHA256, []byte("salt"), "info", 16) + t.Logf("HKDF key: %x", key) + }) + + t.Run("KDA OneStepNoCounter", func(t *testing.T) { + ensureServiceIndicator(t) + key := hkdf.Extract(sha256.New, plaintextSHA256, []byte("salt")) + t.Logf("KDA OneStepNoCounter key: %x", key) + }) + + t.Run("Feedback KDF", func(t *testing.T) { + ensureServiceIndicator(t) + key := hkdf.Expand(sha256.New, plaintextSHA256, "info", 16) + t.Logf("Feedback KDF key: %x", key) + }) + + t.Run("PBKDF", func(t *testing.T) { + ensureServiceIndicator(t) + key, err := pbkdf2.Key(sha256.New, "password", plaintextSHA256, 2, 16) + fatalIfErr(t, err) + t.Logf("PBKDF key: %x", key) + }) + + t.Run("KDF TLS v1.2 CVL", func(t *testing.T) { + ensureServiceIndicator(t) + key := tls12.MasterSecret(sha256.New, plaintextSHA256, []byte("test")) + t.Logf("TLS v1.2 CVL Master Secret: %x", key) + }) + + t.Run("KDF TLS v1.3 CVL", func(t *testing.T) { + ensureServiceIndicator(t) + es := tls13.NewEarlySecret(sha256.New, plaintextSHA256) + hs := es.HandshakeSecret(plaintextSHA256) + ms := hs.MasterSecret() + client := ms.ClientApplicationTrafficSecret(sha256.New()) + server := ms.ServerApplicationTrafficSecret(sha256.New()) + t.Logf("TLS v1.3 CVL Application Traffic Secrets: client %x, server %x", client, server) + }) +} + +func ensureServiceIndicator(t *testing.T) { + fips140.ResetServiceIndicator() + t.Cleanup(func() { + if fips140.ServiceIndicator() { + t.Logf("Service indicator is set") + } else { + t.Errorf("Service indicator is not set") + } + }) +} + +func fatalIfErr(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatal(err) + } +} + func decodeHex(t *testing.T, s string) []byte { t.Helper() s = strings.ReplaceAll(s, " ", "") diff --git a/src/crypto/internal/fips140test/nistec_test.go b/src/crypto/internal/fips140test/nistec_test.go index 3849add7004e2a..3b3de2bc2cb1f6 100644 --- a/src/crypto/internal/fips140test/nistec_test.go +++ b/src/crypto/internal/fips140test/nistec_test.go @@ -251,10 +251,3 @@ func testScalarMult[P nistPoint[P]](t *testing.T, newPoint func() P, c elliptic. }) } } - -func fatalIfErr(t *testing.T, err error) { - t.Helper() - if err != nil { - t.Fatal(err) - } -} diff --git a/src/crypto/internal/fips140test/sshkdf_test.go b/src/crypto/internal/fips140test/sshkdf_test.go index 91135205de9bd5..3538af8469bee8 100644 --- a/src/crypto/internal/fips140test/sshkdf_test.go +++ b/src/crypto/internal/fips140test/sshkdf_test.go @@ -8,23 +8,22 @@ import ( "bytes" "crypto/internal/fips140/ssh" "crypto/sha256" - "encoding/hex" "testing" ) func TestSSHACVPVector(t *testing.T) { // https://github.com/usnistgov/ACVP-Server/blob/3a7333f638/gen-val/json-files/kdf-components-ssh-1.0/prompt.json#L910-L915 - K := fromHex("0000010100E534CD9780786AF19994DD68C3FD7FE1E1F77C3938B2005C49B080CF88A63A44079774A36F23BA4D73470CB318C30524854D2F36BAB9A45AD73DBB3BC5DD39A547F62BC921052E102E37F3DD0CD79A04EB46ACC14B823B326096A89E33E8846624188BB3C8F16B320E7BB8F5EB05F080DCEE244A445DBED3A9F3BA8C373D8BE62CDFE2FC5876F30F90F01F0A55E5251B23E0DBBFCFB1450715E329BB00FB222E850DDB11201460B8AEF3FC8965D3B6D3AFBB885A6C11F308F10211B82EA2028C7A84DD0BB8D5D6AC3A48D0C2B93609269C585E03889DB3621993E7F7C09A007FB6B5C06FFA532B0DBF11F71F740D9CD8FAD2532E21B9423BF3D85EE4E396BE32") - H := fromHex("8FB22F0864960DA5679FD377248E41C2D0390E5AB3BB7955A3B6C588FB75B20D") - sessionID := fromHex("269A512E7B560E13396E0F3F56BDA730E23EE122EE6D59C91C58FB07872BCCCC") + K := decodeHex(t, "0000010100E534CD9780786AF19994DD68C3FD7FE1E1F77C3938B2005C49B080CF88A63A44079774A36F23BA4D73470CB318C30524854D2F36BAB9A45AD73DBB3BC5DD39A547F62BC921052E102E37F3DD0CD79A04EB46ACC14B823B326096A89E33E8846624188BB3C8F16B320E7BB8F5EB05F080DCEE244A445DBED3A9F3BA8C373D8BE62CDFE2FC5876F30F90F01F0A55E5251B23E0DBBFCFB1450715E329BB00FB222E850DDB11201460B8AEF3FC8965D3B6D3AFBB885A6C11F308F10211B82EA2028C7A84DD0BB8D5D6AC3A48D0C2B93609269C585E03889DB3621993E7F7C09A007FB6B5C06FFA532B0DBF11F71F740D9CD8FAD2532E21B9423BF3D85EE4E396BE32") + H := decodeHex(t, "8FB22F0864960DA5679FD377248E41C2D0390E5AB3BB7955A3B6C588FB75B20D") + sessionID := decodeHex(t, "269A512E7B560E13396E0F3F56BDA730E23EE122EE6D59C91C58FB07872BCCCC") // https://github.com/usnistgov/ACVP-Server/blob/3a7333f638/gen-val/json-files/kdf-components-ssh-1.0/expectedResults.json#L1306-L1314 - initialIVClient := fromHex("82321D9FE2ACD958D3F55F4D3FF5C79D") - initialIVServer := fromHex("03F336F61311770BD5346B41E04CDB1F") - encryptionKeyClient := fromHex("20E55008D0120C400F42E5D2E148AB75") - encryptionKeyServer := fromHex("8BF4DEBEC96F4ADBBE5BB43828D56E6D") - integrityKeyClient := fromHex("15F53BCCE2645D0AD1C539C09BF9054AA3A4B10B71E96B9E3A15672405341BB5") - integrityKeyServer := fromHex("00BB773FD63AC7B7281A7B54C130CCAD363EE8928104E67CA5A3211EE3BBAB93") + initialIVClient := decodeHex(t, "82321D9FE2ACD958D3F55F4D3FF5C79D") + initialIVServer := decodeHex(t, "03F336F61311770BD5346B41E04CDB1F") + encryptionKeyClient := decodeHex(t, "20E55008D0120C400F42E5D2E148AB75") + encryptionKeyServer := decodeHex(t, "8BF4DEBEC96F4ADBBE5BB43828D56E6D") + integrityKeyClient := decodeHex(t, "15F53BCCE2645D0AD1C539C09BF9054AA3A4B10B71E96B9E3A15672405341BB5") + integrityKeyServer := decodeHex(t, "00BB773FD63AC7B7281A7B54C130CCAD363EE8928104E67CA5A3211EE3BBAB93") gotIVClient, gotKeyClient, gotIntegrityClient := ssh.Keys( sha256.New, ssh.ClientKeys, K, H, sessionID, 16, 16, 32) @@ -50,11 +49,3 @@ func TestSSHACVPVector(t *testing.T) { t.Errorf("got integrity key server %x, want %x", gotIntegrityServer, integrityKeyServer) } } - -func fromHex(s string) []byte { - b, err := hex.DecodeString(s) - if err != nil { - panic(err) - } - return b -} diff --git a/src/crypto/internal/impl/impl.go b/src/crypto/internal/impl/impl.go index 524db45d749f79..193839f1f197ee 100644 --- a/src/crypto/internal/impl/impl.go +++ b/src/crypto/internal/impl/impl.go @@ -38,6 +38,20 @@ func Register(pkg, name string, available *bool) { }) } +// Packages returns the list of all packages for which alternative +// implementations are registered. +func Packages() []string { + var pkgs []string + seen := make(map[string]bool) + for _, i := range allImplementations { + if !seen[i.Package] { + pkgs = append(pkgs, i.Package) + seen[i.Package] = true + } + } + return pkgs +} + // List returns the names of all alternative implementations registered for the // given package, whether available or not. The implicit base implementation is // not included. diff --git a/src/crypto/internal/sysrand/rand_linux_test.go b/src/crypto/internal/sysrand/rand_linux_test.go index 417523c29ddc9f..ab43904f91296f 100644 --- a/src/crypto/internal/sysrand/rand_linux_test.go +++ b/src/crypto/internal/sysrand/rand_linux_test.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build cgo + package sysrand_test import ( diff --git a/src/crypto/md5/md5.go b/src/crypto/md5/md5.go index 75e1fc7404724a..a0384e175f31bd 100644 --- a/src/crypto/md5/md5.go +++ b/src/crypto/md5/md5.go @@ -104,9 +104,6 @@ func consumeUint32(b []byte) ([]byte, uint32) { // [encoding.BinaryUnmarshaler] to marshal and unmarshal the internal // state of the hash. func New() hash.Hash { - if fips140only.Enabled { - panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") - } d := new(digest) d.Reset() return d @@ -117,6 +114,9 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { + if fips140only.Enabled { + return 0, errors.New("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") + } // Note that we currently call block or blockGeneric // directly (guarded using haveAsm) because this allows // escape analysis to see that p and d don't escape. @@ -158,6 +158,10 @@ func (d *digest) Sum(in []byte) []byte { } func (d *digest) checkSum() [Size]byte { + if fips140only.Enabled { + panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") + } + // Append 0x80 to the end of the message and then append zeros // until the length is a multiple of 56 bytes. Finally append // 8 bytes representing the message length in bits. @@ -184,9 +188,6 @@ func (d *digest) checkSum() [Size]byte { // Sum returns the MD5 checksum of the data. func Sum(data []byte) [Size]byte { - if fips140only.Enabled { - panic("crypto/md5: use of MD5 is not allowed in FIPS 140-only mode") - } var d digest d.Reset() d.Write(data) diff --git a/src/crypto/mlkem/example_test.go b/src/crypto/mlkem/example_test.go new file mode 100644 index 00000000000000..28bf3f29e711ed --- /dev/null +++ b/src/crypto/mlkem/example_test.go @@ -0,0 +1,47 @@ +// Copyright 2024 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. + +package mlkem_test + +import ( + "crypto/mlkem" + "log" +) + +func Example() { + // Alice generates a new key pair and sends the encapsulation key to Bob. + dk, err := mlkem.GenerateKey768() + if err != nil { + log.Fatal(err) + } + encapsulationKey := dk.EncapsulationKey().Bytes() + + // Bob uses the encapsulation key to encapsulate a shared secret, and sends + // back the ciphertext to Alice. + ciphertext := Bob(encapsulationKey) + + // Alice decapsulates the shared secret from the ciphertext. + sharedSecret, err := dk.Decapsulate(ciphertext) + if err != nil { + log.Fatal(err) + } + + // Alice and Bob now share a secret. + _ = sharedSecret +} + +func Bob(encapsulationKey []byte) (ciphertext []byte) { + // Bob encapsulates a shared secret using the encapsulation key. + ek, err := mlkem.NewEncapsulationKey768(encapsulationKey) + if err != nil { + log.Fatal(err) + } + sharedSecret, ciphertext := ek.Encapsulate() + + // Alice and Bob now share a secret. + _ = sharedSecret + + // Bob sends the ciphertext to Alice. + return ciphertext +} diff --git a/src/crypto/mlkem/mlkem.go b/src/crypto/mlkem/mlkem.go new file mode 100644 index 00000000000000..69c0bc571f95a9 --- /dev/null +++ b/src/crypto/mlkem/mlkem.go @@ -0,0 +1,192 @@ +// Copyright 2023 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. + +// Package mlkem implements the quantum-resistant key encapsulation method +// ML-KEM (formerly known as Kyber), as specified in [NIST FIPS 203]. +// +// Most applications should use the ML-KEM-768 parameter set, as implemented by +// [DecapsulationKey768] and [EncapsulationKey768]. +// +// [NIST FIPS 203]: https://doi.org/10.6028/NIST.FIPS.203 +package mlkem + +import "crypto/internal/fips140/mlkem" + +const ( + // SharedKeySize is the size of a shared key produced by ML-KEM. + SharedKeySize = 32 + + // SeedSize is the size of a seed used to generate a decapsulation key. + SeedSize = 64 + + // CiphertextSize768 is the size of a ciphertext produced by ML-KEM-768. + CiphertextSize768 = 1088 + + // EncapsulationKeySize768 is the size of an ML-KEM-768 encapsulation key. + EncapsulationKeySize768 = 1184 + + // CiphertextSize1024 is the size of a ciphertext produced by ML-KEM-1024. + CiphertextSize1024 = 1568 + + // EncapsulationKeySize1024 is the size of an ML-KEM-1024 encapsulation key. + EncapsulationKeySize1024 = 1568 +) + +// DecapsulationKey768 is the secret key used to decapsulate a shared key +// from a ciphertext. It includes various precomputed values. +type DecapsulationKey768 struct { + key *mlkem.DecapsulationKey768 +} + +// GenerateKey768 generates a new decapsulation key, drawing random bytes from +// the default crypto/rand source. The decapsulation key must be kept secret. +func GenerateKey768() (*DecapsulationKey768, error) { + key, err := mlkem.GenerateKey768() + if err != nil { + return nil, err + } + + return &DecapsulationKey768{key}, nil +} + +// NewDecapsulationKey768 expands a decapsulation key from a 64-byte seed in the +// "d || z" form. The seed must be uniformly random. +func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) { + key, err := mlkem.NewDecapsulationKey768(seed) + if err != nil { + return nil, err + } + + return &DecapsulationKey768{key}, nil +} + +// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. +// +// The decapsulation key must be kept secret. +func (dk *DecapsulationKey768) Bytes() []byte { + return dk.key.Bytes() +} + +// Decapsulate generates a shared key from a ciphertext and a decapsulation +// key. If the ciphertext is not valid, Decapsulate returns an error. +// +// The shared key must be kept secret. +func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + return dk.key.Decapsulate(ciphertext) +} + +// EncapsulationKey returns the public encapsulation key necessary to produce +// ciphertexts. +func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { + return &EncapsulationKey768{dk.key.EncapsulationKey()} +} + +// An EncapsulationKey768 is the public key used to produce ciphertexts to be +// decapsulated by the corresponding DecapsulationKey768. +type EncapsulationKey768 struct { + key *mlkem.EncapsulationKey768 +} + +// NewEncapsulationKey768 parses an encapsulation key from its encoded form. If +// the encapsulation key is not valid, NewEncapsulationKey768 returns an error. +func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) { + key, err := mlkem.NewEncapsulationKey768(encapsulationKey) + if err != nil { + return nil, err + } + + return &EncapsulationKey768{key}, nil +} + +// Bytes returns the encapsulation key as a byte slice. +func (ek *EncapsulationKey768) Bytes() []byte { + return ek.key.Bytes() +} + +// Encapsulate generates a shared key and an associated ciphertext from an +// encapsulation key, drawing random bytes from the default crypto/rand source. +// +// The shared key must be kept secret. +func (ek *EncapsulationKey768) Encapsulate() (sharedKey, ciphertext []byte) { + return ek.key.Encapsulate() +} + +// DecapsulationKey1024 is the secret key used to decapsulate a shared key +// from a ciphertext. It includes various precomputed values. +type DecapsulationKey1024 struct { + key *mlkem.DecapsulationKey1024 +} + +// GenerateKey1024 generates a new decapsulation key, drawing random bytes from +// the default crypto/rand source. The decapsulation key must be kept secret. +func GenerateKey1024() (*DecapsulationKey1024, error) { + key, err := mlkem.GenerateKey1024() + if err != nil { + return nil, err + } + + return &DecapsulationKey1024{key}, nil +} + +// NewDecapsulationKey1024 expands a decapsulation key from a 64-byte seed in the +// "d || z" form. The seed must be uniformly random. +func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) { + key, err := mlkem.NewDecapsulationKey1024(seed) + if err != nil { + return nil, err + } + + return &DecapsulationKey1024{key}, nil +} + +// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. +// +// The decapsulation key must be kept secret. +func (dk *DecapsulationKey1024) Bytes() []byte { + return dk.key.Bytes() +} + +// Decapsulate generates a shared key from a ciphertext and a decapsulation +// key. If the ciphertext is not valid, Decapsulate returns an error. +// +// The shared key must be kept secret. +func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { + return dk.key.Decapsulate(ciphertext) +} + +// EncapsulationKey returns the public encapsulation key necessary to produce +// ciphertexts. +func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { + return &EncapsulationKey1024{dk.key.EncapsulationKey()} +} + +// An EncapsulationKey1024 is the public key used to produce ciphertexts to be +// decapsulated by the corresponding DecapsulationKey1024. +type EncapsulationKey1024 struct { + key *mlkem.EncapsulationKey1024 +} + +// NewEncapsulationKey1024 parses an encapsulation key from its encoded form. If +// the encapsulation key is not valid, NewEncapsulationKey1024 returns an error. +func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) { + key, err := mlkem.NewEncapsulationKey1024(encapsulationKey) + if err != nil { + return nil, err + } + + return &EncapsulationKey1024{key}, nil +} + +// Bytes returns the encapsulation key as a byte slice. +func (ek *EncapsulationKey1024) Bytes() []byte { + return ek.key.Bytes() +} + +// Encapsulate generates a shared key and an associated ciphertext from an +// encapsulation key, drawing random bytes from the default crypto/rand source. +// +// The shared key must be kept secret. +func (ek *EncapsulationKey1024) Encapsulate() (sharedKey, ciphertext []byte) { + return ek.key.Encapsulate() +} diff --git a/src/crypto/mlkem/mlkem1024.go b/src/crypto/mlkem/mlkem1024.go deleted file mode 100644 index 530aacf00ff716..00000000000000 --- a/src/crypto/mlkem/mlkem1024.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2023 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. - -package mlkem - -import "crypto/internal/fips140/mlkem" - -const ( - // CiphertextSize1024 is the size of a ciphertext produced by the 1024-bit - // variant of ML-KEM. - CiphertextSize1024 = 1568 - - // EncapsulationKeySize1024 is the size of an encapsulation key for the - // 1024-bit variant of ML-KEM. - EncapsulationKeySize1024 = 1568 -) - -// DecapsulationKey1024 is the secret key used to decapsulate a shared key -// from a ciphertext. It includes various precomputed values. -type DecapsulationKey1024 struct { - key *mlkem.DecapsulationKey1024 -} - -// GenerateKey1024 generates a new decapsulation key, drawing random bytes from -// crypto/rand. The decapsulation key must be kept secret. -func GenerateKey1024() (*DecapsulationKey1024, error) { - key, err := mlkem.GenerateKey1024() - if err != nil { - return nil, err - } - - return &DecapsulationKey1024{key}, nil -} - -// NewDecapsulationKey1024 parses a decapsulation key from a 64-byte seed in the -// "d || z" form. The seed must be uniformly random. -func NewDecapsulationKey1024(seed []byte) (*DecapsulationKey1024, error) { - key, err := mlkem.NewDecapsulationKey1024(seed) - if err != nil { - return nil, err - } - - return &DecapsulationKey1024{key}, nil -} - -// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. -// -// The decapsulation key must be kept secret. -func (dk *DecapsulationKey1024) Bytes() []byte { - return dk.key.Bytes() -} - -// Decapsulate generates a shared key from a ciphertext and a decapsulation -// key. If the ciphertext is not valid, Decapsulate returns an error. -// -// The shared key must be kept secret. -func (dk *DecapsulationKey1024) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { - return dk.key.Decapsulate(ciphertext) -} - -// EncapsulationKey returns the public encapsulation key necessary to produce -// ciphertexts. -func (dk *DecapsulationKey1024) EncapsulationKey() *EncapsulationKey1024 { - return &EncapsulationKey1024{dk.key.EncapsulationKey()} -} - -// An EncapsulationKey1024 is the public key used to produce ciphertexts to be -// decapsulated by the corresponding DecapsulationKey1024. -type EncapsulationKey1024 struct { - key *mlkem.EncapsulationKey1024 -} - -// NewEncapsulationKey1024 parses an encapsulation key from its encoded form. If -// the encapsulation key is not valid, NewEncapsulationKey1024 returns an error. -func NewEncapsulationKey1024(encapsulationKey []byte) (*EncapsulationKey1024, error) { - key, err := mlkem.NewEncapsulationKey1024(encapsulationKey) - if err != nil { - return nil, err - } - - return &EncapsulationKey1024{key}, nil -} - -// Bytes returns the encapsulation key as a byte slice. -func (ek *EncapsulationKey1024) Bytes() []byte { - return ek.key.Bytes() -} - -// Encapsulate generates a shared key and an associated ciphertext from an -// encapsulation key, drawing random bytes from crypto/rand. -// -// The shared key must be kept secret. -func (ek *EncapsulationKey1024) Encapsulate() (ciphertext, sharedKey []byte) { - return ek.key.Encapsulate() -} diff --git a/src/crypto/mlkem/mlkem768.go b/src/crypto/mlkem/mlkem768.go deleted file mode 100644 index d6f5c94171e947..00000000000000 --- a/src/crypto/mlkem/mlkem768.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2023 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. - -// Package mlkem implements the quantum-resistant key encapsulation method -// ML-KEM (formerly known as Kyber), as specified in [NIST FIPS 203]. -// -// [NIST FIPS 203]: https://doi.org/10.6028/NIST.FIPS.203 -package mlkem - -import "crypto/internal/fips140/mlkem" - -const ( - // SharedKeySize is the size of a shared key produced by ML-KEM. - SharedKeySize = 32 - - // SeedSize is the size of a seed used to generate a decapsulation key. - SeedSize = 64 - - // CiphertextSize768 is the size of a ciphertext produced by the 768-bit - // variant of ML-KEM. - CiphertextSize768 = 1088 - - // EncapsulationKeySize768 is the size of an encapsulation key for the - // 768-bit variant of ML-KEM. - EncapsulationKeySize768 = 1184 -) - -// DecapsulationKey768 is the secret key used to decapsulate a shared key -// from a ciphertext. It includes various precomputed values. -type DecapsulationKey768 struct { - key *mlkem.DecapsulationKey768 -} - -// GenerateKey768 generates a new decapsulation key, drawing random bytes from -// crypto/rand. The decapsulation key must be kept secret. -func GenerateKey768() (*DecapsulationKey768, error) { - key, err := mlkem.GenerateKey768() - if err != nil { - return nil, err - } - - return &DecapsulationKey768{key}, nil -} - -// NewDecapsulationKey768 parses a decapsulation key from a 64-byte seed in the -// "d || z" form. The seed must be uniformly random. -func NewDecapsulationKey768(seed []byte) (*DecapsulationKey768, error) { - key, err := mlkem.NewDecapsulationKey768(seed) - if err != nil { - return nil, err - } - - return &DecapsulationKey768{key}, nil -} - -// Bytes returns the decapsulation key as a 64-byte seed in the "d || z" form. -// -// The decapsulation key must be kept secret. -func (dk *DecapsulationKey768) Bytes() []byte { - return dk.key.Bytes() -} - -// Decapsulate generates a shared key from a ciphertext and a decapsulation -// key. If the ciphertext is not valid, Decapsulate returns an error. -// -// The shared key must be kept secret. -func (dk *DecapsulationKey768) Decapsulate(ciphertext []byte) (sharedKey []byte, err error) { - return dk.key.Decapsulate(ciphertext) -} - -// EncapsulationKey returns the public encapsulation key necessary to produce -// ciphertexts. -func (dk *DecapsulationKey768) EncapsulationKey() *EncapsulationKey768 { - return &EncapsulationKey768{dk.key.EncapsulationKey()} -} - -// An EncapsulationKey768 is the public key used to produce ciphertexts to be -// decapsulated by the corresponding DecapsulationKey768. -type EncapsulationKey768 struct { - key *mlkem.EncapsulationKey768 -} - -// NewEncapsulationKey768 parses an encapsulation key from its encoded form. If -// the encapsulation key is not valid, NewEncapsulationKey768 returns an error. -func NewEncapsulationKey768(encapsulationKey []byte) (*EncapsulationKey768, error) { - key, err := mlkem.NewEncapsulationKey768(encapsulationKey) - if err != nil { - return nil, err - } - - return &EncapsulationKey768{key}, nil -} - -// Bytes returns the encapsulation key as a byte slice. -func (ek *EncapsulationKey768) Bytes() []byte { - return ek.key.Bytes() -} - -// Encapsulate generates a shared key and an associated ciphertext from an -// encapsulation key, drawing random bytes from crypto/rand. -// -// The shared key must be kept secret. -func (ek *EncapsulationKey768) Encapsulate() (ciphertext, sharedKey []byte) { - return ek.key.Encapsulate() -} diff --git a/src/crypto/mlkem/mlkem_test.go b/src/crypto/mlkem/mlkem_test.go index ddc52dab975d32..207d6d48c3cb0b 100644 --- a/src/crypto/mlkem/mlkem_test.go +++ b/src/crypto/mlkem/mlkem_test.go @@ -43,7 +43,7 @@ func testRoundTrip[E encapsulationKey, D decapsulationKey[E]]( t.Fatal(err) } ek := dk.EncapsulationKey() - c, Ke := ek.Encapsulate() + Ke, c := ek.Encapsulate() Kd, err := dk.Decapsulate(c) if err != nil { t.Fatal(err) @@ -66,7 +66,7 @@ func testRoundTrip[E encapsulationKey, D decapsulationKey[E]]( if !bytes.Equal(dk.Bytes(), dk1.Bytes()) { t.Fail() } - c1, Ke1 := ek1.Encapsulate() + Ke1, c1 := ek1.Encapsulate() Kd1, err := dk1.Decapsulate(c1) if err != nil { t.Fatal(err) @@ -86,7 +86,7 @@ func testRoundTrip[E encapsulationKey, D decapsulationKey[E]]( t.Fail() } - c2, Ke2 := dk.EncapsulationKey().Encapsulate() + Ke2, c2 := dk.EncapsulationKey().Encapsulate() if bytes.Equal(c, c2) { t.Fail() } @@ -115,7 +115,7 @@ func testBadLengths[E encapsulationKey, D decapsulationKey[E]]( } ek := dk.EncapsulationKey() ekBytes := dk.EncapsulationKey().Bytes() - c, _ := ek.Encapsulate() + _, c := ek.Encapsulate() for i := 0; i < len(dkBytes)-1; i++ { if _, err := newDecapsulationKey(dkBytes[:i]); err == nil { @@ -189,7 +189,7 @@ func TestAccumulated(t *testing.T) { o.Write(ek.Bytes()) s.Read(msg[:]) - ct, k := ek.key.EncapsulateInternal(&msg) + k, ct := ek.key.EncapsulateInternal(&msg) o.Write(ct) o.Write(k) @@ -244,7 +244,7 @@ func BenchmarkEncaps(b *testing.B) { if err != nil { b.Fatal(err) } - c, K := ek.key.EncapsulateInternal(&m) + K, c := ek.key.EncapsulateInternal(&m) sink ^= c[0] ^ K[0] } } @@ -255,7 +255,7 @@ func BenchmarkDecaps(b *testing.B) { b.Fatal(err) } ek := dk.EncapsulationKey() - c, _ := ek.Encapsulate() + _, c := ek.Encapsulate() b.ResetTimer() for i := 0; i < b.N; i++ { K, _ := dk.Decapsulate(c) @@ -270,7 +270,7 @@ func BenchmarkRoundTrip(b *testing.B) { } ek := dk.EncapsulationKey() ekBytes := ek.Bytes() - c, _ := ek.Encapsulate() + _, c := ek.Encapsulate() if err != nil { b.Fatal(err) } @@ -296,7 +296,7 @@ func BenchmarkRoundTrip(b *testing.B) { if err != nil { b.Fatal(err) } - cS, Ks := ek.Encapsulate() + Ks, cS := ek.Encapsulate() if err != nil { b.Fatal(err) } diff --git a/src/crypto/pbkdf2/pbkdf2.go b/src/crypto/pbkdf2/pbkdf2.go index 0fdd9e822d40a5..dd5fc33f2120c3 100644 --- a/src/crypto/pbkdf2/pbkdf2.go +++ b/src/crypto/pbkdf2/pbkdf2.go @@ -2,24 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* -Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC -2898 / PKCS #5 v2.0. - -A key derivation function is useful when encrypting data based on a password -or any other not-fully-random data. It uses a pseudorandom function to derive -a secure encryption key based on the password. - -While v2.0 of the standard defines only one pseudorandom function to use, -HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved -Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To -choose, you can pass the `New` functions from the different SHA packages to -pbkdf2.Key. -*/ +// Package pbkdf2 implements the key derivation function PBKDF2 as defined in +// RFC 8018 (PKCS #5 v2.1). +// +// A key derivation function is useful when encrypting data based on a password +// or any other not-fully-random data. It uses a pseudorandom function to derive +// a secure encryption key based on the password. package pbkdf2 import ( "crypto/internal/fips140/pbkdf2" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "errors" "hash" @@ -41,7 +34,11 @@ import ( // // Using a higher iteration count will increase the cost of an exhaustive // search but will also make derivation proportionally slower. +// +// keyLength must be a positive integer between 1 and (2^32 - 1) * h.Size(). +// Setting keyLength to a value outside of this range will result in an error. func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) { + fh := fips140hash.UnwrapNew(h) if fips140only.Enabled { if keyLength < 112/8 { return nil, errors.New("crypto/pbkdf2: use of keys shorter than 112 bits is not allowed in FIPS 140-only mode") @@ -49,9 +46,9 @@ func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyL if len(salt) < 128/8 { return nil, errors.New("crypto/pbkdf2: use of salts shorter than 128 bits is not allowed in FIPS 140-only mode") } - if !fips140only.ApprovedHash(h()) { + if !fips140only.ApprovedHash(fh()) { return nil, errors.New("crypto/pbkdf2: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") } } - return pbkdf2.Key(h, password, salt, iter, keyLength) + return pbkdf2.Key(fh, password, salt, iter, keyLength) } diff --git a/src/crypto/pbkdf2/pbkdf2_test.go b/src/crypto/pbkdf2/pbkdf2_test.go index 03980c7e54d3be..eb0ed14e243c6b 100644 --- a/src/crypto/pbkdf2/pbkdf2_test.go +++ b/src/crypto/pbkdf2/pbkdf2_test.go @@ -221,3 +221,33 @@ func TestPBKDF2ServiceIndicator(t *testing.T) { t.Error("FIPS service indicator should not be set") } } + +func TestMaxKeyLength(t *testing.T) { + // This error cannot be triggered on platforms where int is 31 bits (i.e. + // 32-bit platforms), since the max value for keyLength is 1<<31-1 and + // 1<<31-1 * hLen will always be less than 1<<32-1 * hLen. + keySize := int64(1<<63 - 1) + if int64(int(keySize)) != keySize { + t.Skip("cannot be replicated on platforms where int is 31 bits") + } + _, err := pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, int(keySize)) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with extremely large keyLength") + } + keySize = int64(1<<32-1) * (sha256.Size + 1) + _, err = pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, int(keySize)) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with extremely large keyLength") + } +} + +func TestZeroKeyLength(t *testing.T) { + _, err := pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, 0) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with zero keyLength") + } + _, err = pbkdf2.Key(sha256.New, "password", []byte("salt"), 1, -1) + if err == nil { + t.Fatal("expected pbkdf2.Key to fail with negative keyLength") + } +} diff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go index 5dd875e6e72575..1ca16caa9563e6 100644 --- a/src/crypto/rand/rand.go +++ b/src/crypto/rand/rand.go @@ -38,7 +38,9 @@ func init() { Reader = &reader{} } -type reader struct{} +type reader struct { + drbg.DefaultReader +} func (r *reader) Read(b []byte) (n int, err error) { boring.Unreachable() diff --git a/src/crypto/rsa/fips.go b/src/crypto/rsa/fips.go index bc23d597098f0c..8373c125ae3096 100644 --- a/src/crypto/rsa/fips.go +++ b/src/crypto/rsa/fips.go @@ -8,6 +8,7 @@ import ( "crypto" "crypto/internal/boring" "crypto/internal/fips140/rsa" + "crypto/internal/fips140hash" "crypto/internal/fips140only" "errors" "hash" @@ -17,6 +18,9 @@ import ( const ( // PSSSaltLengthAuto causes the salt in a PSS signature to be as large // as possible when signing, and to be auto-detected when verifying. + // + // When signing in FIPS 140-3 mode, the salt length is capped at the length + // of the hash function used in the signature. PSSSaltLengthAuto = 0 // PSSSaltLengthEqualsHash causes the salt length to equal the length // of the hash used in the signature. @@ -61,12 +65,6 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, if err := checkPublicKeySize(&priv.PublicKey); err != nil { return nil, err } - if err := checkFIPS140OnlyPrivateKey(priv); err != nil { - return nil, err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } if opts != nil && opts.Hash != 0 { hash = opts.Hash @@ -81,14 +79,25 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, } boring.UnreachableExceptTests() + h := fips140hash.Unwrap(hash.New()) + + if err := checkFIPS140OnlyPrivateKey(priv); err != nil { + return nil, err + } + if fips140only.Enabled && !fips140only.ApprovedHash(h) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(rand) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } + k, err := fipsPrivateKey(priv) if err != nil { return nil, err } - h := hash.New() saltLength := opts.saltLength() - if fips140only.Enabled && saltLength > hash.Size() { + if fips140only.Enabled && saltLength > h.Size() { return nil, errors.New("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode") } switch saltLength { @@ -98,7 +107,7 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, return nil, fipsError(err) } case PSSSaltLengthEqualsHash: - saltLength = hash.Size() + saltLength = h.Size() default: // If we get here saltLength is either > 0 or < -1, in the // latter case we fail out. @@ -123,12 +132,6 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts if err := checkPublicKeySize(pub); err != nil { return err } - if err := checkFIPS140OnlyPublicKey(pub); err != nil { - return err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } if boring.Enabled { bkey, err := boringPublicKey(pub) @@ -141,22 +144,31 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts return nil } + h := fips140hash.Unwrap(hash.New()) + + if err := checkFIPS140OnlyPublicKey(pub); err != nil { + return err + } + if fips140only.Enabled && !fips140only.ApprovedHash(h) { + return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + k, err := fipsPublicKey(pub) if err != nil { return err } saltLength := opts.saltLength() - if fips140only.Enabled && saltLength > hash.Size() { + if fips140only.Enabled && saltLength > h.Size() { return errors.New("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode") } switch saltLength { case PSSSaltLengthAuto: - return fipsError(rsa.VerifyPSS(k, hash.New(), digest, sig)) + return fipsError(rsa.VerifyPSS(k, h, digest, sig)) case PSSSaltLengthEqualsHash: - return fipsError(rsa.VerifyPSSWithSaltLength(k, hash.New(), digest, sig, hash.Size())) + return fipsError(rsa.VerifyPSSWithSaltLength(k, h, digest, sig, h.Size())) default: - return fipsError(rsa.VerifyPSSWithSaltLength(k, hash.New(), digest, sig, saltLength)) + return fipsError(rsa.VerifyPSSWithSaltLength(k, h, digest, sig, saltLength)) } } @@ -182,12 +194,6 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l if err := checkPublicKeySize(pub); err != nil { return nil, err } - if err := checkFIPS140OnlyPublicKey(pub); err != nil { - return nil, err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } defer hash.Reset() @@ -205,6 +211,18 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l } boring.UnreachableExceptTests() + hash = fips140hash.Unwrap(hash) + + if err := checkFIPS140OnlyPublicKey(pub); err != nil { + return nil, err + } + if fips140only.Enabled && !fips140only.ApprovedHash(hash) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } + k, err := fipsPublicKey(pub) if err != nil { return nil, err @@ -231,14 +249,6 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l if err := checkPublicKeySize(&priv.PublicKey); err != nil { return nil, err } - if err := checkFIPS140OnlyPrivateKey(priv); err != nil { - return nil, err - } - if fips140only.Enabled { - if !fips140only.ApprovedHash(hash) || !fips140only.ApprovedHash(mgfHash) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } - } if boring.Enabled { k := priv.Size() @@ -257,6 +267,18 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l return out, nil } + hash = fips140hash.Unwrap(hash) + mgfHash = fips140hash.Unwrap(mgfHash) + + if err := checkFIPS140OnlyPrivateKey(priv); err != nil { + return nil, err + } + if fips140only.Enabled { + if !fips140only.ApprovedHash(hash) || !fips140only.ApprovedHash(mgfHash) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + } + k, err := fipsPrivateKey(priv) if err != nil { return nil, err @@ -289,12 +311,6 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ if err := checkPublicKeySize(&priv.PublicKey); err != nil { return nil, err } - if err := checkFIPS140OnlyPrivateKey(priv); err != nil { - return nil, err - } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } if boring.Enabled { bkey, err := boringPrivateKey(priv) @@ -304,6 +320,13 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ return boring.SignRSAPKCS1v15(bkey, hash, hashed) } + if err := checkFIPS140OnlyPrivateKey(priv); err != nil { + return nil, err + } + if fips140only.Enabled && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { + return nil, errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + k, err := fipsPrivateKey(priv) if err != nil { return nil, err @@ -320,15 +343,17 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [ // The inputs are not considered confidential, and may leak through timing side // channels, or if an attacker has control of part of the inputs. func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error { - if err := checkPublicKeySize(pub); err != nil { - return err + var hashName string + if hash != crypto.Hash(0) { + if len(hashed) != hash.Size() { + return errors.New("crypto/rsa: input must be hashed message") + } + hashName = hash.String() } - if err := checkFIPS140OnlyPublicKey(pub); err != nil { + + if err := checkPublicKeySize(pub); err != nil { return err } - if fips140only.Enabled && !fips140only.ApprovedHash(hash.New()) { - return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") - } if boring.Enabled { bkey, err := boringPublicKey(pub) @@ -341,17 +366,17 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) return nil } + if err := checkFIPS140OnlyPublicKey(pub); err != nil { + return err + } + if fips140only.Enabled && !fips140only.ApprovedHash(fips140hash.Unwrap(hash.New())) { + return errors.New("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode") + } + k, err := fipsPublicKey(pub) if err != nil { return err } - var hashName string - if hash != crypto.Hash(0) { - if len(hashed) != hash.Size() { - return errors.New("crypto/rsa: input must be hashed message") - } - hashName = hash.String() - } return fipsError(rsa.VerifyPKCS1v15(k, hashName, hashed, sig)) } diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index 0f58f2226f848e..95bb4becd2ff8c 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -322,8 +322,26 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { if fips140only.Enabled && bits%2 == 1 { return nil, errors.New("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode") } + if fips140only.Enabled && !fips140only.ApprovedRandomReader(random) { + return nil, errors.New("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode") + } k, err := rsa.GenerateKey(random, bits) + if bits < 256 && err != nil { + // Toy-sized keys have a non-negligible chance of hitting two hard + // failure cases: p == q and d <= 2^(nlen / 2). + // + // Since these are impossible to hit for real keys, we don't want to + // make the production code path more complex and harder to think about + // to handle them. + // + // Instead, just rerun the whole process a total of 8 times, which + // brings the chance of failure for 32-bit keys down to the same as for + // 256-bit keys. + for i := 1; i < 8 && err != nil; i++ { + k, err = rsa.GenerateKey(random, bits) + } + } if err != nil { return nil, err } diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go index c395732c8b27b8..795439d1c13df0 100644 --- a/src/crypto/rsa/rsa_test.go +++ b/src/crypto/rsa/rsa_test.go @@ -10,17 +10,19 @@ import ( "crypto" "crypto/internal/boring" "crypto/internal/cryptotest" - "crypto/internal/fips140" "crypto/rand" . "crypto/rsa" "crypto/sha1" "crypto/sha256" "crypto/sha512" "crypto/x509" + "encoding/hex" "encoding/pem" "flag" "fmt" + "io" "math/big" + "os" "strings" "testing" ) @@ -102,7 +104,7 @@ func TestImpossibleKeyGeneration(t *testing.T) { // This test ensures that trying to generate or validate toy RSA keys // doesn't enter an infinite loop or panic. t.Setenv("GODEBUG", "rsa1024min=0") - for i := 0; i < 128; i++ { + for i := 0; i < 32; i++ { GenerateKey(rand.Reader, i) GenerateMultiPrimeKey(rand.Reader, 3, i) GenerateMultiPrimeKey(rand.Reader, 4, i) @@ -110,6 +112,23 @@ func TestImpossibleKeyGeneration(t *testing.T) { } } +func TestTinyKeyGeneration(t *testing.T) { + // Toy-sized keys can randomly hit hard failures in GenerateKey. + if testing.Short() { + t.Skip("skipping in short mode") + } + t.Setenv("GODEBUG", "rsa1024min=0") + for range 10000 { + k, err := GenerateKey(rand.Reader, 32) + if err != nil { + t.Fatalf("GenerateKey(32): %v", err) + } + if err := k.Validate(); err != nil { + t.Fatalf("Validate(32): %v", err) + } + } +} + func TestGnuTLSKey(t *testing.T) { t.Setenv("GODEBUG", "rsa1024min=0") // This is a key generated by `certtool --generate-privkey --bits 128`. @@ -185,7 +204,7 @@ func TestEverything(t *testing.T) { } t.Setenv("GODEBUG", "rsa1024min=0") - min := 128 + min := 32 max := 560 // any smaller than this and not all tests will run if *allFlag { max = 2048 @@ -679,19 +698,48 @@ func BenchmarkEncryptOAEP(b *testing.B) { } func BenchmarkSignPKCS1v15(b *testing.B) { - b.Run("2048", func(b *testing.B) { - hashed := sha256.Sum256([]byte("testing")) + b.Run("2048", func(b *testing.B) { benchmarkSignPKCS1v15(b, test2048Key) }) + b.Run("2048/noprecomp/OnlyD", func(b *testing.B) { + benchmarkSignPKCS1v15(b, &PrivateKey{ + PublicKey: test2048Key.PublicKey, + D: test2048Key.D, + }) + }) + b.Run("2048/noprecomp/Primes", func(b *testing.B) { + benchmarkSignPKCS1v15(b, &PrivateKey{ + PublicKey: test2048Key.PublicKey, + D: test2048Key.D, + Primes: test2048Key.Primes, + }) + }) + // This is different from "2048" because it's only the public precomputed + // values, and not the crypto/internal/fips140/rsa.PrivateKey. + b.Run("2048/noprecomp/AllValues", func(b *testing.B) { + benchmarkSignPKCS1v15(b, &PrivateKey{ + PublicKey: test2048Key.PublicKey, + D: test2048Key.D, + Primes: test2048Key.Primes, + Precomputed: PrecomputedValues{ + Dp: test2048Key.Precomputed.Dp, + Dq: test2048Key.Precomputed.Dq, + Qinv: test2048Key.Precomputed.Qinv, + }, + }) + }) +} - var sink byte - b.ResetTimer() - for i := 0; i < b.N; i++ { - s, err := SignPKCS1v15(rand.Reader, test2048Key, crypto.SHA256, hashed[:]) - if err != nil { - b.Fatal(err) - } - sink ^= s[0] +func benchmarkSignPKCS1v15(b *testing.B, k *PrivateKey) { + hashed := sha256.Sum256([]byte("testing")) + + var sink byte + b.ResetTimer() + for i := 0; i < b.N; i++ { + s, err := SignPKCS1v15(rand.Reader, k, crypto.SHA256, hashed[:]) + if err != nil { + b.Fatal(err) } - }) + sink ^= s[0] + } } func BenchmarkVerifyPKCS1v15(b *testing.B) { @@ -746,28 +794,70 @@ func BenchmarkVerifyPSS(b *testing.B) { }) } -func BenchmarkGenerateKey(b *testing.B) { +func BenchmarkParsePKCS8PrivateKey(b *testing.B) { b.Run("2048", func(b *testing.B) { + p, _ := pem.Decode([]byte(test2048KeyPEM)) + b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := GenerateKey(rand.Reader, 2048); err != nil { + if _, err := x509.ParsePKCS8PrivateKey(p.Bytes); err != nil { b.Fatal(err) } } }) } -func BenchmarkParsePKCS8PrivateKey(b *testing.B) { +func BenchmarkGenerateKey(b *testing.B) { b.Run("2048", func(b *testing.B) { - p, _ := pem.Decode([]byte(test2048KeyPEM)) - b.ResetTimer() - for i := 0; i < b.N; i++ { - if _, err := x509.ParsePKCS8PrivateKey(p.Bytes); err != nil { + primes, err := os.ReadFile("testdata/keygen2048.txt") + if err != nil { + b.Fatal(err) + } + for b.Loop() { + r := &testPrimeReader{primes: string(primes)} + if _, err := GenerateKey(r, 2048); err != nil { b.Fatal(err) } } }) } +// testPrimeReader feeds prime candidates from a text file, +// one per line in hex, to GenerateKey. +type testPrimeReader struct { + primes string +} + +func (r *testPrimeReader) Read(p []byte) (n int, err error) { + // Neutralize randutil.MaybeReadByte. + // + // DO NOT COPY this. We *will* break you. We can do this because we're + // in the standard library, and can update this along with the + // GenerateKey implementation if necessary. + // + // You have been warned. + if len(p) == 1 { + return 1, nil + } + + var line string + for line == "" || line[0] == '#' { + var ok bool + line, r.primes, ok = strings.Cut(r.primes, "\n") + if !ok { + return 0, io.EOF + } + } + b, err := hex.DecodeString(line) + if err != nil { + return 0, err + } + if len(p) != len(b) { + return 0, fmt.Errorf("unexpected read length: %d", len(p)) + } + copy(p, b) + return len(p), nil +} + type testEncryptOAEPMessage struct { in []byte seed []byte @@ -782,9 +872,6 @@ type testEncryptOAEPStruct struct { } func TestEncryptOAEP(t *testing.T) { - if fips140.Enabled { - t.Skip("FIPS mode overrides the deterministic random source") - } sha1 := sha1.New() n := new(big.Int) for i, test := range testEncryptOAEPData { diff --git a/src/crypto/rsa/testdata/keygen2048.txt b/src/crypto/rsa/testdata/keygen2048.txt new file mode 100644 index 00000000000000..31854020d75b01 --- /dev/null +++ b/src/crypto/rsa/testdata/keygen2048.txt @@ -0,0 +1,719 @@ +# Prime candidates for RSA 2048 key generation, one per line, in big endian hex. +# This file contains two primes, one of which is at the end of the file. +# The totients of the primes are coprime with 65537. +# +# The number of composites, the distribution of their small divisors, and the +# number of trailing zeros are all chosen to be representative of the expected +# average key generation run, to provide a useful benchmark target. +# +# https://c2sp.org/CCTV/keygen#rsa-key-generation-benchmark +# https://words.filippo.io/rsa-keygen-bench/ + +c3280c027a24d6e2277d1227e4531c1ee765bda9d304bfe7a59519bf2686fd5435c5e5a1200b74cba47c49f444e89b0e0991d05824119ee9dee73e4bd6ce3e93a10604239677fd0b735438b2360d0da9e6e929e1c564df0f0287eb0804cb9dba824a53156098ce8c8efdb8197d12ac040baef4e90710670aa7a33d9a0cd0c62d +d658a5dea2c73517326aba6bddc24b677d47178dc77dd584160827702d203908c504bcb305309c6ced76c7bb2f891bd99119e95bd3d90b1c25c00808c10f94e5ee29aa84d066baf7a17250328286a74173cee433292afec2e3707790d1d590b0ae8d667614a8ec33ed8cd9344dae48dff98631c7e609b7c63c4bb1a5dcdcfdb5 +e5ce50dd929f658d7285fa47a175298c0657dfcc98620e92772c6ed6134ba24d7703ce31d6a2e20e82b95be2a72f3818f7f2bad597bed7a8b5c6c3cf408fb49aa70c003182583a95b9c615ffff31343060c98b1058423387f52e3a7992a29b5b494a49256a7dccb25874f9109d7124ddb2c3e6f4430243439b605ba67a1c2f8d +d01fc442afdb8bd631d93d4531bb16f4f9c3bc710f6546c248f809987cb7878e8ab9dec73b7d3d823563e4df53e41830b16d25eb96985f393c17906caa24d5a8a8123439ecea4d34d7b28bc4f16bcdcbf1f2d3a8d53b5c26cb2d217c04a12f3a436510031901bb1af9c5530402384ca801dff5ab6ea922be78857d0290ae11bd +c0047306b9a1a51226ae3d77981ef08f43783f5ae6534ef5783e0044518e53a7c778759f88378970c1ac71e3a57a6f0839e45036d3dd180a713d962f701b139af22c8fe07ae2105c1b56efe0cdb4ef36e94d9b398570db68efe873ccac75186357ffdb5f9e10470e62efd78b09ae76508ddef6c43faeff84cbf8ceef9138e035 +fdac5080d27a1a4213ee721dc344e16b5e8e6bb790e1bb8dc965a2e76b2311889fbaa9967b57d323aaf13e2960dedac6ac719a1a85a6817262703606b176bf3675922502040273ce256939000801cd1dd8888251c14b6223dd572d4c9efee5b522b2a06b0ccb34d302524eacf1f9183350c90adf8f5ccdaf708937f25677817d +c4876a812cbb5fdb31dd80e04656a0bd3e131a2d2eed4b29420f373e9e4ac61a0d0a023713c030bcef917c146647f97aa9937626f251f1fe2eb4106f3eeecb6b8c2346ddc9bd88740e2115b23b72d2d4a4ece93f96ec34b983c61cce8c33bb650107859190c2bfb24b326a534aa0f44fc2210601f39c82b344e184632d825315 +ecfd96ea0fdf3cd7072d6db2d9ae052f50a16b948838353f9d566c383309fcc1e43d3a0ed9cdb9c9d4b0d8552053a1c2561c7df175bae082128c3d380a60c8c578b51049928d0669bc2f20256b80b089f217f390c80252c49038bd1f2f89b265d49b935c5d621a7f9e6b41e8c7c5e454c5b539f90bfd2e5e09ff19ae280c92b5 +d934a84e3d322798b83c86259f93f06f9545a2d5cb911ce4ee19fa887e433baaf6cd3c66285626dc09669b4b2209926193af341683629bc8575f0d3cdff3b558566733aa3e256f4363f4eb4005caf7cef91d2653e20f7b2c35a74411dd73e8be0d1193f0bdfef5f39b680d069d64b3c49de3d31e91e19015b3e14e24884010d5 +dd1f0316593bb2e1df22f1fd618d56e7c4f28d6a85501ce14e262dd316acfdc67d17caaa3d7fbb730c885ef394050eb80ce597cde2510153e67d99ab8c463f6b8d3aa607e49d8303318f9c379df7f48454b9b1093ff29f0a6e78f586d00f26912b2f27db810b96c956586ebafcbee500a701f74cdc42201aabc2c5a1977671ed +e1eda3a00453828b2060ac037e3fdc546a019c30d7ef08c51ec722ef98313148b08583fc495746ab3e6a46efb4f4342013ef44da8821adcde73bfc873b7a521dee1e9cccf3f169e78fba89963a4561fc507c5333befa509cd910ed26e45c62a2a3e0df1d961042e6f38474a99c9c5b366d0a6d81c41774d1ffc4117cb9f000bd +f04402a78861bdb7100df31330193151688bd1c4ecbff887c1e484d67ec705cc62013d49f1b92fef400d5530202dcb4145828a9f657d2a1c454dafcb3b36a1664027ff703d78fbf4712fc6248308b56ca80bd10b19263df5194a74278269b61f5199390d4f77a9934cb88272681a77682dca4af9cdbcf376ab7660b9f3e6007d +faba6e112b7f349d92bba5f80de2437ad692922a33efed347579eb7be667b1df09a2dcdfac534fee0fbd4fef0838a74006dbcba62b8ce6cc0e6d1e1035effa096085716e4761083728f923525c9ac4339d15cbce8164d2abacaf1e134802e28733d1f7e7327f7430f4386102915f02b9c435b7a4f8a0433062973356490c999d +cd1d62358df14d823c9aaef80de713db2bff6dc3f15da163f981601b2096cbbc04ee12767937ff5f7218b758defc27912cbcfd8a7bdffa48e1c5d4accd21ded2cbb99f053103555cd78e9cbe0ce6e89db7fea4718b326f266c62ca997c1102ad9c04a3296405d2f9b8ed4a0e799e4818eba66247c25da68955f03f076dd7f735 +e4ce1badc198ea490b37dc7c04e6748904868129bb8cd4a7638f5cd9b7cc5e7f6f92639faa0bd755c6b84bb8c8361c5869b248ec443b24a81e5a9abf94309653f9777dca00aed9fcecc21dad79f9aa5b1a720d3800728a452d5a4fd4dcf156c20fcbba7e0a10c5c3cc63bd6e24fa63b248ed1a9b24205a801be3c1cb74be2bed +e6212b34639e14987d4820c32930bb2a72ec98670dd8269ffb85583f86be8152b6421798e6f11f64c410966f5fc85074b932944675c1d2e67ef1e831f15cec8dd2b8f7635e84c1624cc830fcf3ce1df011dcf133bb636e2a5c92789bf6e824a0a84c9311d0903af0ccc2ee417400538b745190546f97abb03461efcd924af65d +e64458a6209dfa4288e20607c5efa3ef0023005dd04286bd9d97e835e6316c8b76a6cd471d74d30fae6f84a0605135d85d5b626a9bda82ff24c1245428be2ab06ed0fe994f6bd4189e5620d88dfafb454dcee861b2cdf7362bbc4d2adafd606c17088f190e6bd14d57b51fcb1068b13cef8c454e138694f06138dc7b082dcdad +c0896f1090bcc0994a8888a71d7aa6a484d4a02312a2fffa805bce038a98064f054e097488c9e2e3f8daed458cca270761d07daeab428591d6bc30debf0548949ab2c89b2a4950241f78798100efe858b7667842410091764b6f62061c22e1b2f923de098547632f81525ed7696808097d20c7703b6ae01e11c094aee01a39cd +fe882662e021d8b4329b80e6caacc66174c483c453881389de36331a81b229c8404b4916a8103139fb125289a3746e3ef11370b6453b170635b727e88f3ba7ff760db3d69b87a6613f41a6a0d2af65326b109d6b4fb940b3dd20f1920bf40b88bd6027a11e9bd7e6e1508ca1c45d5011e084085aba7898c439aa9fff2466cf75 +e87cb70f7bba008257d76ae1b6629a6a21d12158eef819d8422a8b4f18830d426d784ee64d204fbbe3d21fc6bcfcb946a08b72e92132563de4f52c7e6682785408b0f612d7a461f413b68a186c637872a6207aaeb3774d302053375b1ea36bc63a968eccbb6034d408d4fd82c93810bd50990d28b5ca037fb541cc0402770a65 +c59ce336ad22514ff1bfcb64706d7812f8fb592fada9adac89b7b4bc74cc100bb7d41f6448a4fe8e8c54ad3eb9f643ff62240f1f24e4f7869c4de12cc7530f8856f5abbbc098a09a0b6f98dbf008ee586eed521b608302c49ff5450117babdcc89c1e1868503557696b7ea13ac4587d1535982cd0d88aac6161bbd3338877785 +d8f6e37189f694e33286bf6489fa969c0b82e21fa3a7fcdd142070bcc05a43b89b55163db9077a3576cb8917c30a3a66a04262e6d065cd5e74a6064de059c502c768edc7e33647f66ab6fd024d3c900b552dfb6c2d6aa21b6c1b569ae31bd75b2bc4d968ae9c49009c770f57d121a9c3ff0ed376f424891e734456bf605a943d +dd66fb98c6b659ed158375316e61785260961deda5c7cd3a620c21da10382f4aa97d02c1107bfa0589fe6362bb883f48dd5ece326f3c7cbc28d91ab7d1fa8ea1a0df6704bf3dfe9b1f053e4e307d5bed6bb7576254a8188f1d60d91963dcc2db5f7723ebfba4adcef5c5de4e56484306da72669305e167fd2224e678edb0c5a5 +c5a1245c78e99291db45c225773d2141b204c9c8627dd8120c4cc331a67c0b2c118e20dd7805436b4f05b36ad6b1d20b128286976e8f61772f2635ff88fb1c1f6ac2f390175eb9f4f556babe6aa1b2be9683ed442261a79789b10c5ab0f91eef7cc7d6b99d0fccf0d8a9780e540a3ece093ce6dcbfe8e62bce995c23ff201c65 +cb8ca7cd1d043349c6b244ea7f7766f717daf97fe5db079c9f94c0a30ee9b7fd6999728fa22802ac02c7e3228b53030956d3a7c6c148620461adc434388eb9b90ddc84bfd6032069a425521548c2cb78b4683c80d2c48c6df5e831fc23731957d490193f15607c396f62ad650c4266b8bd46b0127001b7d36b97e3b0f76ad735 +e89e37f2ae5007402036195b40ebd882f60756b616a827a3267a701cb12a7000723ba42eda4619989a8d080a444e5a8a8d4fd1a635279297f420e20d9e2587391613c9c35c0e4f6135f5c3abed32fe28783e05a2e0253e7bc351e20c4ddb399aa24ebd971fcf9db2263b0d01b628e87650d14cafe341f881b010089052bde375 +ecc17a8b65380f0203aad09dc6fb76d31fbf9d75a2e342a6b8bbd80585f649d1c1fb6ab5db10e2c15353c9baf32730ea797c9743a8e64e5ec31dfd776b0a25b2b80b68ecd612211b90233f2662381eba397fe5b2bd2cf330c1c7a3abaeca21a13ee088a8e24175022df26221de4cbe1357ae6cb0c93e973779c5b23c8707759d +cc377422aa2d5b267df8adeb9e774012bd8ab7e86855deb61466882d7ee02a749426d242d3329ed6a0b98ce56caff22b2a2ec5c1d7565bc7ac1062b3c9bb887184d690a4c39987e706c9b0360a9195bae763ecd9cb4863b7020972aea212df48374bac14101073af8169f8113debe9480af26dbe534ff7ebc078a84fea0452dd +f6b8e7c33db4bf1106731a54905cc34ebb8235ef9c8196ae9a43afc58f9fc478cf45924130526d2b3721cd9e5397665159d9e978676ac5663838bee9db236e389ff21786737b78abb5c34dd44f878a6a8f38b4f9017a8429e0ff7efc7e95783d532681bbb652222e604492e5e9cae70acff964a27274e30f3544758a60603c85 +f45323a64eb82a494ddf732502daf879938c2000b2f68690b013cb3a40d7459d850c41c829fb0d830a8a8ce53e43040c0e2de84e36c8adcf8b2935fc4d603d5065b7530baa21f3fc8566d13f6840dc0000e0531819c833ddb9db52257b1b18d815adf7f2cc1a74a8a2417db14f3578582987324ea66a85d6877e2803df34976d +c4b49ac4794fcad37ffeddf43b27b0f95d504ebd8faec1640fb3d1ec3ecf0d5f433e53f62b436a17436104fd1368903e38a10e173267b2105bfe5f64be5c275f3e1574cd2bc46435b004c158df4eb0ea47d3244a16e2d4eb49b68f06212c3753ba6acb74e59ad0154509ad46491b167f68c118f2ab8ea351f22cf23946b98545 +d9966e5ef2c98ed3ab7665493cb3b29e0b55e4f3593218417dc012d0cb6293a3ec9440f2959f3990afd04def8fd14e016d949c0ae116900fbd3e810addc5545db1285f73927c7828eac881a6bb5c25410ed7a9b28e9bc3a6e96f1a239dfbe2e9db90df2cc1bd832293f06e30b4cebf795945b717ec0d273364fd603a2db06b5d +e0fcd8c3c66391647f3cad90bc667361230cd0907a4f95d63b85dd7ce259952784a2facfd8a707dd58c5a66f1f3f238fd55f6c39a615547448d01ed27c046357edf1c7c8efb17e529a9a28b63e83aa0d203cc687cbfe19201388ca4edfc302fa04e11dca78907ee5401bca9b9ad1650ae8dcd63921aacd3afafe39cc9fc0e18d +c2e60320538adaf01aad0431695c391db850f09bdcd1d7711224ff6117e966c3ff1df74fe32210f805dfe5b9ede44b3f5b4398ba09f41712a0acfd17d3f29d901531870079a2106331127a4ee083b348ae34eb10be594f24f320315535cdaaf40ae6c0a3ed762e5f4f77aa6ccd01a1a8a471e2790210485bfb2179f1b214157d +cf2bea1f18b8b7c173139634d3a75be5b16cb92c08fd01189a75d214a0467380b64eaadc717871926d76a3dbf60c488cb1555eccdf749bb161c6f55bf68c6bce9e89ca4d4165cfeea41e5d7ca0bec8b5e32fe8a4f433a0b94e34949dd60f3a73e108ab3ed1cca9f1bca7a28f111ed8ca5b88253df61d7921d1e53de8839cd4d5 +d846b3f003220a8c32229f05a7cbca5fa373ac6d5b6dc3cc2bca8f0c1ed73092087cff694c43ef25b00e16b3327127259e48c51ac271faf2c6c8d4745f07a08b4df84aeaa95d0a73ede4eb50264198625f39917ca61787e8441f579e75a370a11b51db89851783daf44cc88142af0c558f346ee033317235085a2cd185e0e5cd +c82518eb29d04d44fc4d8ed9cdf5252652db5214be69172f58ebba11ecf8ccbdc76e45054484b270b559aed27f66c97f105b997e83f969d2b8045fc5c4795a0f14aa2f26d66a376245ecc3ba39dfa105044754ca7ed6b96722963168f565f192e40fa1f0bd4c875d325394eb72c13f53af85814aed81db2ccdc76feb4bdfcacd +cd174cce3c2a5a2aa08ed861ad64fb0c6a03407ee93ed2343676f1cafc25193af66589d891a1aac648b4d309b047ff6b116dc7bd974acde138cb95c4d7ee95737aad0742aa8d1a9e1781b7cf2a6f09495eb22d2767412a6743f2d80dbe8ec2b07ff4ea528c13e66a6f90385975f8d441aba9c0db2085b7714e07636914878655 +fdd7dbf9cec219fc1c59527f14b95b1094107a0173afd4435582319719f86d300c59fe8e676e29c4b90953208683694ac537b1bada7696767e0fb69893c102da2ad292318a8c06623844d2baf98ce5cbb8f5f4aac2b8adf1ba6cfb0b0238816d76f48671a0bd1aa42ebddcb5ca36bdc6b163f2fdf808ab06ce8129c88a34a29d +fa040fe20e7648d5452eb22d0449d909131d8aca9afb85d969c029525f06d97c719b5427f767b0c64374a0e85059287903827bf981af82b073909ecc277085e1bd489d8416f5e78584565fac47a39551991fcd6850d3705948630b4777eab04ec4d8211bc174b4f52a02aaf3176817c461aa64c0cc6f8caa83c2dae9785413f5 +eda819394c12ddfad1c5195f590389019ccebdceb5d22bdf8f0b8039f339cfa920726ae970d02788062f05e3f94601f6cc73caa1385d12e26842e8658c4feb352a77fe30cbe16475919ed604306151ff9519500547f02c5baf6d0ffc28c145eb09b64b6513bcc006cffa11b1ce9550eaaca8b52abab2731f0a87c2cb3838bbfd +e0a102fad6c51e75f45f919ff5c994fbedbc2decb9be980fb643c5dc4e8d7715642daa68e907ef73d50f48d9b22f9dd269b587edb7daf5086bf9d524de5b0ced07ec6ddcdbb6a7b80a2811a041b43d80ef92d80d26f626712921b2f454b16f9ce469979f9020058df410a8c0fa58f79a1ab8909258d136ea7565cce3db5583b5 +e29cac64588f2d390d569480051aa315a28651b17b5debe2f11937c590f7a6acf553bfd700d56ca0e9557e0002f8078e5c459dcd68621a7684938324207e686d5641210d9c25030892671e28dddbbfb8a5592162a9988e6201916cde8d4c19469edcbf2d539c9d13225ed75642edaf5a5a41f70498f6ad7bcce75521c56ec055 +c8f655d328cd97dddb6eb73dae1c513547e1f9eb924c9d3ab85c12e93f55e7585bd78605483a941d93c951ecb7703f70acfd134898f2d8c3b2eb852694feb12a536b68b1b31df677bcbc1818d00cec3ec77c69dfba074b773458fb688d166147942c76d2e90fe3bf6b1c63062bfa85591216a3a9a1fa89762c1c9d0941c1c5f5 +e4e8eb15317afd379b4739eef0e964d365f45e52838aae4c04671bc51ab06aa583fe7efa805f3beba9bd6c94d9cf1c4816d1d30a6cd0603d8e9e08d367125e6d70b3d1f4bac36d2d81442136d06bdb68618a895ecf5a45d27c9bab3f5302bf93c770680fb74d79fc3ebb0cb23c70a2c798b30e4341bfeb0ae224d9adee129aad +e9b15d8bc80ca4b280c5ff6ee0c92de7efe5d70c356855de5e23fbc6d7c3fbb9ebb2018cc8d3c82ec24ea057005aeb8504d68579c13aaa90adcb5719b8cc633e19ff70ed00f5ea38230eee0fb6c0185ad5a946798b8477e5e0a40961891ca48da0e92db23ee17549212cb337094a9074759544226d88b6a4ad0795f97adad15d +d7a679cb982749b84280e8e68593a578b1df3c3f3cea32c5f85cb8ad9e916a2e46804709b7d13ba3fedafcd5363eed258513077f8618c187a5ead4d80e365afd07ca9b94790e9ff312b7fbfa363571cbed16f7ecbc4958c56d8d2834a80841d55d200fbdd68d6f2edfd53ad9bd98b87467bb33f75fbf679805c691892180f6b5 +cc223cfc403e44f7b1b6b1b4923f5e601174288ebb449c591c7acd53629fd1d54e713a06f3f0c5bae6e0ec6c60f63a39628ad674607ebde06ded53451758cd3f46a2804625ccb5c6b4b8d3387863ff0f191f241ebc76ef2c002ae25415844b968887f5afc058a8a84a73c00708956bb108e22afc824481bc5f635ceb5b612df5 +c8cebb9900596d86f78b675e0b9b6e2efa078d5338e2e8c411b686eaf9fdf2009b0ddc390a54f9ed042363a55faaf61b1db8f81138701bec4ed242a96116111baa7a3a1a58f290ead2dfac414451ca6349b40a006cdaf8a7279413b774da1ec860e60d4cf8bd496c4daf6d67a071672b5813ed77e8cdc39463bbe9e833fc80b5 +ff877d7617c7692de75b95c6aa2b938e06917fe3964679d4d99909571a4aea438d5d9d749ea1cfe9edc3ea5ecaeb2cbf67df63e1d77f2e99841d52401fab7dc8dc8e7db0bb4bab56c2a738c0567fdd9e1d5cad05f143d9a208f1d3c261e2ea1fafb523e45b7ba00d3aa14f8d0ea6843f978289988325b778a74a503afc03dc75 +cdcbd7112b042912bd54805d11c334564f5b49da681c0300afbbf0657f50655b73775b8494160160c721758f848b8f5fdce80accbff0a819779a3ea8a081d2c8204e1ffe325bb2bb20646a3e99e0afb359fac49747724d30fb2861d4fa3576e157ea7ca9a5f8acf8d978a680cf20eac31c46ce888214306d279257c61372182d +cc8bfc877a0f189775fd8663bdda40e940dede0b46a834cb46e57e08ee7ee103275eda18677b67a7d15aefb25311e15bbb8320c72794426976af19f61709b596318d6a181afdbdce9f663e48da148de140c099b10cd8ad5c56befe57b612a3543ca80ae0b1828da26f30498d4b685d3a3bf0d9821aee4eeff19dcd095312861d +f57c84d6f79dabd40b86d9abb9b211355dee5a80404a0858450122d5c9b7f8850aa9399fbe271fd3d551b3fc5cb1f965e9f7ef04d725f8be2fa1473bd2925541b006934939bbb4cc6a061b12c9c4b2b6c3e72c05c9ec303ed089e71cc33b7954004cf5ea29c43dc207da28ce80feb805c804a371adde587970c4a6eb4c1b04c5 +d1be804b2c5442d6f2b8a6410d7b499e8a992a72fa4d532f9715f3c24abc6be301d5eeb7156d8b2010b0b61978ae0bc4205b3b9fff4d0bcc9842d43ee99b56d741d7c06b70b9928f9455802e6b714d01622ad14112f78ced761f10393fdba4e27626638c5b578da4db161f3e4d52edd897cf0dca2c83f1bca4422af1325fc82d +c3e6cc9373a12a14d3916be237b0b016fe7f240c9e7c631e50d2078d63552a898c781d5c1d23d4a4d4881ec211af661527fac39d5937e9f84bdb8182e69fce9a2a31618480bc538cc14e3d778cb0b7b3ba5ff2e4ca04282bda5df1333ed840e437b2ba8daa1679b4288f95ea19b5051ee77fc0dffbeed396df58796803b7eb95 +d36abf4625822935c2e14f94141ac1ab6a700f62010d9d5fb56ec9d582fe5e8ccbbace8496b16c215575df4692fdb5e5603cf0ca4680d9cc2e9440896221c86c0d3d7a96dba443d2eb651e95326d2663d8777c724c8b75be3e3b2319b23ee3561748560be33e8e4fafe01b40490732ff991a7c0f971672fa60f7adb59ecc3475 +ed3d82f546fb076502e0f5c4db6089fd203a6f8cfb7a3000e8c0d98ca15fdc784c4ffe0331bbf753663f7754c52886c6175465114a0d009829843629dea455d6ca5e940d88e1fbddab15ba018ae467a7bb2ac7fd9df12707611134a10e6b008f52bb0d2c3889a9b6dd8695fb7287bd2644d38094222df4b6e0a3799d95a32cd5 +e9faa255b3a1b7a6514cf14b7a62fa3ef57c1e7c62943aabc0ae690ed22c656d7477e17a42bd104e4243909576d03f93805c50873b8af7b6ad4a67350842c84ef4fff257d7c7b21dea8a907bf2be9bfa7261dcb50cb6d80ac1ee9e045027d0d6c4760541cbec65db502eac75e340555b2607779fbd4550218c0b8f8303692e5d +c3995bb359e84240b30d6927e9999a3c8c31577b42dbd527dea888466f209724156f57b3392566ea36066eb321924d8bdc5868c6a3fd546c413f31a3a85e8cde6977c4253af146c8acdafd179a5f0356d6219f3db83d565ce1c1286b5083b9226c982641ffbd5cf76738eac94b55ff7ff37817afc68761499762525ac97d8b05 +df98867af5ade57a001271bed2114f69b037ce084870e224d7b0b0e1af1f911bd91e50cfb6017706bf027c0031ebf403420690dd73ea6b862ad2340a1281063a52c647e793d852ee7d610de95d8eb418c9d091e8651f8a9f4c2becd70ee006abdd439e38a773373ed57210044143472bac3ab9d587b84ea9d166f61b9c91c1ad +d799dc56a7286200cfc1959e434161e67551f5b74a92e515466ab1332df4332b9e07af5803cfdd4aea7e6b76f24049cf402fabbdb4529c180d63b1fba3badb09b23eae4bc8784baf2487236b94684663e3bde39acd41c875ebdf1e5143a55fd15be61b2991bc24f9fed25d9779c75176f597755f6f0d2a95211f4756c1516fb5 +c6310962a28f5ef6bbae4ad219aaed7a5fd3021b64ef4f92b3c0ecd605cad3a9f97c8a8019724432318b1e65452a95b3e8a91d2fcd4a0a72fe084cee5c526b4ffb8ce02e2a394fc1dd4c4781aa06be5c2e55ed015c15c4f4b7c408af2ecabaa3b2909b427960571eb7cbbd9bb18a9751b6a92935d41e9f7176c97b64ed55354d +e5192872a3d1e009df8a07cfb29361e86c95183d9702d02d257bb5bbec7830e44f14ca100ba2a59fb3dec712ff6c274f592a45daac1e5c2b4e31b737ff1a57ac338e4e3fac6dac68671e3b7a6d5ee21cfd8604c04bed23a45c00ea0b7d0d1f122af3229ca667ee8808f86fe2e740c016729ca1082873f71ec822f145d9b76aa5 +e44d270ef565b5e7e27244463c6ff3bfaca373f5248970a917e3c76f80f274f328793273323bcc30f8779dfa3454787931c7f7a21ebf41b8532b8d7dc86c1f67edd627e7425aa3b86b3f865c848a8c5941ee098caa2f307436e886983552c6aef44c25d65186e1d0ca31d32a9f784a7e9d42d704c331cb4bf2815c084b356315 +e0edc4ddc48bfbd3d7509a7a9aac58c5538218b81f4c3eaab6fa616954c1d19458271a91925dd6a2da62cf33fc137687c3027fbd671d8c272d4d74e4afd343530e9a62a90de771d7340cb3acaf85444c3a861b7f73f0f1194d9f1bc72482b7926348d41ddc5d74286349cd7570fcff35521f0747ec8d3cee3d6ffafd61402c4d +cd5729475237d63b4dba8e30ebb93d68a0a867e2e4b7d624117adb20708c61102b0ddd4fc2c14001ec81fa9638f6bab94afd4ed51b3dc669ed3aa800b6b9c52a396f93a24b0715dbe17a1a8ed2d773c373570a415647794b28a9e4fd450a6a3f4e6af36dbf31201f194ccd6b09905982e01d5e56a743f4507645b84a28435b75 +f629fb24dc8ead8c4fd6b2a95c080d547d158c17f19ab53deb4eefe36857a5a1ade5fa87db6928a3b0b1cf9dad51819d93f4ae71d3881a26e54cbb10216a63687f2a6dee8ef58cf6d8602b8b394e6b9495874b8f92aa88e2edd842ca9174290c411f7e582ec2a691968ccec291d849d3f2d0834aae803b77839bcdf42f3bc97d +f8b09d6e5cee1df04800701b4583c8b963e34224d6c9566bfd83b11afe5d8b1b26255918ea42df2c389f5c355476ae841391282399de66a055d02bcf1cc71e1b22d1903e4dcbe0bdeb478a9949690cd694fb86ba6d7e83ed73e7a739486d5219cdb38a7781a5144476f01818db6abd1d73be57b0355231f24ea0154c20f5050d +f4163ed4e0ddf79fd97c43604c8e7b0ee09580b62166b6735c55ff4fd849c938716c0d129f5874dbe070724a0e4186a9a196677e1a7c52b9f34a117d51fb97281446ff7f3336b225ed287e4dd82b84c024e88047292d18e061dee2f7a9ec5a8f81a7717071a2f86bd53b80f68243bdf19e446232951265c065a60fdd487b7c3d +e1145d235521795fbcecf10eb4ca8a29cbeebad436d82540ab3eac21b46f46891772b08a7e69b1ea1c504047b0f584b47c600e18a6d7511715048c4e68a6c54e1af0ded19d84c83f84aadc1bf6ba030f2f80afe29adb094eb68a76e8cc9c8caa95e51c11fc590c90aa86945456b72324cf7035d13e4a1ea7f39cd265964e2ac5 +f0f245183e09ce283c77f3e17e0cb4ceebf99c23e4433a1241a68829f0709cd929068f8984b0bef578198ac06f3a3a753697695447d0a675d7a897ead5a99ddc0a444b4e6eb568932886ae1cc3a67647a248a3ac95e941517da3a20e9bb301102bf28d812434358035901eb99568b8982ef9a76cea58e874c686b9d1bb7e6cb5 +ea13168958667f214be4cf775d29789ca2851c20360aa05e76b5caa7d1ea4e6208ebaaef15dc9eb2b0cf54a9211bc0d615c72d251f361f861d0edfcdacc8c8a71cdcde40baa97d8febb5220fc253b53759b208a6905e00ee4970334270777dccfef096274fffe21699a37a187d4e771dc33d21c8a39e479b1ae4e820561aa5c5 +e666c27528aeebef0bcabad66873b3b44385315ec52177a68d6ad4fe86eb0307ec0aee39d7e671e8dfe432e31ae226cd99e3c3af73b9740de75674621abe63a510b264c2d6597e0963be2257130cdc4c510ac5001370b22d7edb060619dc525380b97787853a80f51492e7d75285a106d4b1f386593b4a8deb46baac39acb12d +dcb4379101c0d50c12a85f4efa5e8d5a3765de6f646060458db7469cd2fe0ebf620d38c64b0a6720080df8859ac74d3a5209e9a59e1c2437860b58a0ff8d88c279b164ff5f817612475cab07c966b275647cc3b765347c21b431a690765afa60f0158da8101214f28d034318a86bf9b592cef2abaae616017e2d5a046edb752d +ea5dab735eb313fae23a38a2aa217c62cd7d67a00a5a56fea73ad5ddd38c4668ad89a4cab1f94f3aeaa1ae7edb27518026e41111b513a74a4d88b3ee9df0bcd991e8adabe8ad233b0fc0bff82d0c87f010314e8a069af98b2d6aa922a8e7792e8a07fd61da0f8325fad04daedde33ad9558e193eeea4bf35a73ad21995f02c55 +c189d07afc0ff09ed9fa600f2b16bc6ade9a253420079dcdc5107946ab42e42a00b2728b4baa608f6ae6376b2c7eab0b15824efc1f9c5eb320f42c837524bc9979322fb29458d40dd65c5c1e629170827876dbf1f37e774d71225ac3a548f4e8126add70d76d6bc802f84484aa402a0dd379f7ea2c9a9d5488bf26c38b46267d +d74d8e36181e5ac15aa040c54bf8e64386aad135468efbcc1cb17c9db0da80a233ee78cbb83c8411c7632955e1e5c151b5631d1bc32683975c9db251e3c8eb3336e7bd888c9607795e040b1eeb85a59180371f7089f9f19e66073a32af1d9c23004cb656eb37955b4672ba85fd707934d0018202d02af9a292fdc537d9335add +eeedecf01b04430f99072f20643d57b88bc4b3d1ab69ae1db4b1867ba7ed56d50fd9ec9f06be6acc702b2ca193147343483c83f77894d98af2f36e6695505ac43c9cce9a6fecc94fcecaa89360bf407b28b6332db5dc5449ea9c68b227ff1abe73a612e4df4632a5d8dff54d721671ceb603ce0750e2ee8ffa2f7b82b3faa015 +db4ffa9ff038ad29458284e3137601bca912bc18344cba7385c3fc73d2d072fbd0942fcd9048321d37ad8c7c1cac6b431c87624639859685302dc830d671e7189a64d3fea5461dacc06ea617a037af08845f86b2907fa3bf7d2308806694c44ef29002a735b58802200391c3a2ab88ac9cf8ec437fb8068902160534788ad4d5 +f49318745e60ce9c01a772f6c051f1b9fd0c1218ef56a442ab235d6e7c6eddd21a564b22f1d587222bc4fd062e668d24b17802da698e2364720a7c1675fb7982972f7cb583eb4deac0baa2d74ebe3759517e9823bbe50bcc3bd94d7b175928f268d95e68e734c6a7e7c9e14b52afc3d2745b4af669f1bf436793cb08a1d5acad +d2fb4ef724dbf7fcddd0ba8686c697f5c64a869c70efc258cfcbecb68e0bd27c5269018b957604f4adc46124e28078a40b410d2b6fedae4f91892d172204327cb4e71f9c6f89b2d2b23f1c40b7389205471358e89db6851829f9b9fdf6f7736dad4ccec19830e38f4f5f4dc5ea9ddf3e26cce3cadc2cfb30752682e4a829ab75 +d5daa77d5cf5869c9c83f3048a11ad7cb39aa6ab44b353c9b7d5a4121aba52954fef42bbd60d475f361af181322f0919b9b1d82339dffb7eb74cd800e1ed8f71aacc66b6fa67613e82e40f522e662fc27604e129ca42c311e4994711e143459d21c89e07a7d14c02a85abbdbf2e32f9b559d9f24a27a98b2521d39acd6c690b5 +eafcb4e1299d16e398fa574e354d79cdb9625797688faefaac2d4a29b23ef552823c7e19d22fef28e2c1a24c6bfa374063ee99fb069b97f8bc472af6d69ced556f13efea086266bdff71c1b4da8e4b3f85e1a72fcf562a70f198c7608328c9277d85247311acc83a98866a521b7fb9aa0a7f4043109746274b624d8dbefd4745 +c7e8eb5f561d14193634b83e7944b8149e67f8bef30702c3a04acff906d4481134d4755317cfbdc39043dfa9d7b4c0f98e5b01a4f50ef49c7d17ad7c4164d1bd9a504de03bd419b33980cb0d0938f0e6aef5a7e69c1da4a31b4e8398eb00208f0175bd1c4f46e4496580dfc26bd9ed2715a9672e144e07e49f44e91f68a0b8bd +f08ec66071a5b495fa35b81ebc06eaf3fcfeacb3fd78b753e5de57cdeee8be92bb77db13d1b62490f29e6c1821e54cd30d83b54cd9e9eefc349c7cb4333b67e692fd459ed9f2591166693dda538bc39084ddf85014a38b4df0e88350ced2cceab85fc2be8b14888889f7c2d5e9683674f40610855d77ea09f89d705ca753702d +c2140c54f9dbf6e1f412a27fb4d6c11b58434a1ece009dd1dd820ef05f59dc0da64f05d96be8b9d2b27c6f9f5e49cbccba9b16fe1babf5eee02fe432ad86c04182371d3514a36f416197b9524203c2919b2c79ad55467ecaa56095e3048830b63ccec9efe120272b6ed318804cdad84a62f99bb63464981f552c655babf51435 +d4b69020109685911be78c377d4bd7fec5443a91ea13266a0c8286b0f1289b6df4b3d451ae92c5dfc1c60c9ef38a381ec9257b65f984e1e364e6c060198f3e0823405aa93abd8ddb7daddbd1e467df6b7b749fb31e4edf88fef8d222de401a42bc180fc5c5dda9d7a494c8c88359072381727a59a3ee715035441595e622edc5 +d2b5c8d40f92c7f7af2933c5e7a120977d26637932c98ab9b034c53074ea38ed1279e7e71b062b8d140e3d499f96a2cf696980043611ce272475099dff5002bde2c6f9c936b4764e0adaeb84ac9554f81b8de3e195a56d9314660d6ec76438d6ed9c8a84a2cd1d08c50ca5d12392df85ccf70467ea0407a3985dbbee5293ce7d +d2cccb950749662ae7599688015675cbbbcf7966a03d22dc8af81f4258a1b78e9b9d7bf8b5d6983256a98af801721a139df8cfdcd29e576af1634a7c682e016acdb7a510d058d4514be5b50e733cb9d30c7b0bb33ea184a00f8b3871c72ae3bbb37d9238e2988fbbbce08c2d4a065d0c63e80c7cf40f15535eb55e4f50d360b5 +e81853fbc8f1fd6896a47a6e80539439cd9645e8b0ded1f5c554cf853e617bc0b411d15ca04bb9b22fabd4aa2226df4c119d59ce36ab116166dcaf03129c94f7db155e8638c8d309bfcd64fab7c77e6a35b3d2668a40f02618820f51a5ff241c8887066a45ff2fd930e1d5a6a18e9e9351adfa05dd462dc7c9382fc7494b54b5 +de9f654dde79f9170defd2ad8386a4ab535889fcefc088eeb1addf4aa45a01a0a4d583e40109a9637aaa8c7fcd5314570bef2ff3b8958e80f2fc146c71b3fd79ccd4d06d0a7be94eac5ccb5fd44695df6d78ee4dc7a9a6c51852b95219b9b296a10d5ddffb8f38ac5034351565caadc01881d3575709a0e9e818151852816bed +f89cae294cee1cceaf47188aaacab9ef8e42b584dc85456f370aa6d729e28d018da252093e35c555f242c44c40e1e531dc1f1fb4fc955ef4dcbb13a340adf0c7101e1afd8d11cc65993fe209ea8886855c132b6fc257e4190356375cb8bc004598415f51cf5d952707e42ac26c85c184990906c26f9215b9353a22cb82e2acbd +fa89c077b8fd463079fbb1c9c89b2891e6afc11380297be421cb5a7ff390385686879041f70358f1db73ebc8df6a494393f0303e5c560a3fa2aba714395a20398aa953dca5fde0d901e1bb617790e1f0094203b5b1ab164d86783955e5826fc803cb72c3c6bc77ee9477d3eb7df476e6df97a4b20e59facf160c2ad0f35beb95 +ebfc6e1f84e61274faaf64e51d06e188c3efad1947ba41027041b56e3a1c80dd71281759d7a75ec704e22d9e205673ad6a38f0fa0d936cac6e3c2754b1f0f12946f4ac36f3248880e2d7f57635e80cc27c12b8016864d02a04e5236f537eba37f7d4fc5b46af2d042702110c9f1ece18d74879f776e3b21bf76adcc8617e8fed +de9ad428d3d9b64ed58c7b59ae9fce61cb2c5ec3f95b5955341f67ce61c0db17394fbe596b5ed306a293c2ec9591b5f5bcea5b396bddbbf56f435edc1b4484de6310975688210197160c945b7ee3ebf6b7c9848318be0e45899d364fc67d4ffea82479fd5f672f8df5f89ba157ff7ce6953e9ecc685b5f50f629de639f372a7d +c87d59cbe55203e3b5ea1bee1489173612a0e52d9c366a7e09a0575b0e79ff5d07f3adcc468018a2ca24b974025d2b26220c2a110537de158dcdaf860e8e7e1685e47f8327ce81ad8244385c88c477add2c8b997b3014ffa32a952e179acebd87fa545e91d5a56d4a16e41e50ed5e3d5dbbbd7be870944b2984afc2d57607db5 +d82c4ed9f5670b8b68d2ad054794f78a0b3fdb32e88db4f72f75033983bd1884fa8bf5b3a1427805b2f4d4787b6a1547876a01c8a9c2c74799d5f7c6e0f0482967c02db243be59c78e4fa4f5a34e4895a07ffcca0ec3bd5f24048d10ec4e1bec3945d7280d8144b5672a76746f6c15e42970eb55c840687a915a464cf4b4795d +d500a754e5fad4d58a926c0d18159eade6b51ec72a6445cb782a9954c54176bd7d70d3a60fe5279757a739e6062eb9cfa83e3520e1964f59def2d9808b32da1468af321ba7be5a3bc619b5c75c636efaa86d360e8fa0675f3dcdc4438c417bb51902c9d3565e37c799fab1b94f17854ca143533795ebbcc614bca54f36959c4d +d4d9363f9db9a5e0bc14649810f5c435a731f6a3fffe42b24d8df6a6eedd5ad0562f17be5db9950d46407bb9dbba9f9a3ae29fe3cd6f8d04595ea26849bd35d49ee0e8359c47cf5776b51567c82ac6cef6bec5fc4e1d5f5e712a1a9e8b449e6a577a4104b385bbefeab0fa580ad8b5fe8712779b5391635f8fec655c59d656f5 +cfbd0ea0320f61a647b842dee1bddc13ec5de423c1be6fc590c31e8069884a5f87c37d7e0a2a2ac6fb34188f91bcba00e2b87b65002e2ee25461c2e2ed35a09d7914fd0779d3bb46748a530ace5c5006e200301b9e26d3d7738668b0b1fe5602b7579b7b4a904117010957e55daaba9baed75af81537ddff4d20f16351786c1d +d8c02acc14cc767fa7b05c3a06207d5c861262b850ba868193d9f82b7aec7d9cb656175af823171ae4c0776319a067daf1c7ff913432f4d0133c2b75076e800d6de11debcaf71f6113679e4bc16ee76a3de6b191f6d3cad0f9d2ec82cf415c8412fb2213f0ef13a67d3898ae4ae1166d439c9cf4b9e20f2c2df83319e042c3e5 +e09ab5e7d7b2e37af36a0efa852cf2dd2d0c3356d28a81a8d860745806998293b9c337c88bdcc1c20b41d26f50ce1db86a67c5b08d5bc7caf5082b1f6b9732f8fd6e362d6fc9f3c7dea1babd701a97b41c901320e21cad45dfacfbffd70f2d7484c2df74903b13cceb7c16a3b694c1f3e598185fdcc2fc57e2c6e5a37eb84dbd +e5f48e4163389097f7f2557916abcd1f9312212df0707edef38698f90010aa7b700da25b6c8d3b514d6446b4e91db6f8c17eae2819104f969f30a07e9112ca1ec98ac4461dccaf667a48a552a51a5eb1acbd7a75f0538ff2eb6d8bd2c3b320fccec7a318731274edb1a09b6116eae22be0f3fdc914e462d2575cca09f0ae3365 +e402a052a7a9b2cfa8242c5d82973bae666edb84875b46fc25f708d2b102ec88ffc81e775dd01ff7f80f86974fc5c628e3857827466858b6a8cf5b1609beb4ac5a2c295ea1f07b678a4c5f4c9a9279b91cd314b6bba8ea68243beba2754d4be2159ccc1937d8a65d79934e78fb70c26ef9aeb114cec8aea0c82cb5ede771f8bd +c71fff2a5b3ff0e26dc3822532912b9853215321f7c64f7d9c185021164778ccd16d948dc71373d5ada2dee3e8dc0f9c4504d013d5222e2a04b3c566c31959ca85aadb18a843de651a1280f28c3b171495cd3748d24b21e1d5c05bfabf7a718c88a6af4d4eba69abb4eb558c15ec988708d0e10131e8c7d0d11ea6c64423eb3d +d7cbf7cc537909068a0722855a09cf12b51530e87916e6c89696ae56e23e6fae0788e2bf78c6547bd121e333c76ea86ab23d732b793a2c402799162c44ce27951c1ea985ffcffd7b0f94242b295bdbaf3b8b7082066757d7d9b8c38e4134fb6006fcd89a00e4ecb97da349e5f9f93950e973ce136e0c5b07c4bb1e79fbca0485 +d864ee108f8b62a1d71199d384358b2db77aa201c67689fb2c3034e3eb7fae2fab138756a59809d330e55bd15f41451b1d106a25db63d9df8ddbe26798c03062f0f7dd8e1f1ae43fbec8ac08f919f348090a6b74abed675d9f55028fd9ac991a32ef3b12678d3bf7f6e63705cfde4ca9f0fa864f693e635502ff578828bfb70d +df728818bc24b802d4928b3fed26c687bf8d4da2af081b0a5b7e8408d0e1e2de2afd236decc06e8b58d43e064765535741273691c905a083424bf82ab2ae83563a7c00d43814220569aa54615e7205551bb79fcf36af9ea8c30799bc37e9ecdecb90355a92e5d9d62328059eea240b67e36573b51d11c9df248190839afccfcd +d740d1962a65467ddaafb33dcec0247d6a0d3815d7a4b1f3da3e8ed0ca75e19dc02b16a71dba5fa28d09f38ac61352e0a870f8bfa094b459f5bea40cf42fbb17567211fb0af831b0c2112154762650da96d76b73b58c460f837ab8f8918a2a081f7a04716d85a87f4676404b03b467781ace02ec88ec890464c13688dee050ad +e1762495c9983ae9e73ed7458e667553ad221d6e6b675764f89331005ce0186c6d308d3ef1f911244bf4ce0d588505f7b5559bbee448d8373c51c8ea98ed1f3b3d112f4fed7d560db106c96c5fcb6687779ec1f6f472a6703a70cbfd2d31d607ed5e2149d19204ccae35988684b60c54292091c6510e23e36653d3925b14b525 +f311353a315073e78a275aaabf28914051637a5cb165eb300498365c72c6d1e67bd2510916b31ad0518c52e79e19915d9a361624d4aae90e852ae780c1d43edcea4c27304fbc9c120fff5566e361d9d696c99fd01a09580526292e5f626a8318a6af6f34665971dffba3f676d64b4a9e3cd8d6601de4f049c54e6a69f4fb0d35 +c610a2f93bb2952f6173ffc5cfbac1fcb866c4a61d2d7ce84932b63e5d1cd84ff22f00bc64bcc134e012d0e960c022c20fd406e8b1ed4c50f74a684e081a347e8dd8530f8e4c8bce1eac6532738d067e04d30b74f594ceeba4fdc49c3bc92e5c002b63798edc61b7cfc4a83c8d8e264c094f4d68b81d3be22db9d1d63049989d +dc83d0f2c3f7f9c7fe861259e876a071245fff737b963c686b45d421f094e61df3659c9de88d764072995971fbc688f03c5ba77170ed1786b9aa91b312cc008a9d46cbf48328dcf7a8a3ea5a172b88e16a6de0635770f3cbc47fae8904dec0f998fd814a7415e9f8601938cfd23f38aa9c0972c5712651834a23627676d6d98d +f8a26d32aa3c0a5a555fd3d63856e2dd04a1621e7caa100d3d775c67d864a6f7fca4f0564f5866cea4b6ae6597c9e81c7a67ebc8e7cfead05a511bbe1aa998630e763e4bae47d05c98b6242b32511260fa834b5912970b94ddd996bb712d361c2ed4117a6a7e53543b9b8911eacccfcb4d58d7d4b60d8f4b261b0a426125619d +da00566848c01f70b68f38c714ef36b49121604e951988e32e30b0649b15e5732918c5c7d830bc081a2b1bb3a16340e9fe8e6b4683734fff68c018e85f58f1ff30b54cb047e6abf634efcbdf080f6438e4817fdcc4c10f3003d8528502704c09e99b6a65ce88c100164e9931f4ba544c3deb5690357a0bf5f1cd9b5c6f5f9bed +fa5a96c584235b346dfa9355e3fef370e71edbc85826c3c202b460fe60c72a460c1d0aa5a4668ee910e8058c6ddc20e1f30f5b6a369cc5e2e262001388fecdfb030a01dfcc830f8095e808c439e32c1c22666b81f5c061ee279069c67efa9f905ecd04261da47539e10d1545d76f602acc4eed83275755523d8107dced594d8d +c9c78f76f133f80c60cb1ad3413b7c40afa5da7859ca268b899a75ed06f09d85dc62d054d50d9b2f060f1930bd150e6b0c7e71a2bf0fcf3d02c7425ed17a0b3673d3f4f9437c8375302c8c5f35d8409bccf4eec3438d7d0aab52c25be9706fd376d91cf1e2cd43ba19a07b76dabdeeb2650115499630fd7458aa2d42e128051d +f05036e195b7ff5750867782fe51d704d8717cb0c9ee23d598fa0d2d7db8f1105685495cb095a05a9557ca4dc8fd7948dcf362715637bfb171e5038217469bb646029155af6e83821d82d50a4c8064a2496778e8c073fca484d2d2367c52bf39cdd98f1f2bff5c2362f79ccf871a70db99a404d33b1fe6615d9dcf64b2b2286d +e6389326de9873443429aa1213f8f39322c4dec5f62d5b4fb938db5ef185adcdb29f5d4acaa7b784fbf9abadac995e43b5298c2a06f34c681e08e29d100f240676c8d798b141335afa8fa06e8e102edcd542318dfa190c981be80058855125b1e1bc4e860864a6b2daad46293d166e5ac6d33634e6157731aab64406aa198775 +e28708b6c2ef0e6eb7854970f88cf767a58ef73422bacbe3c74e208b22133ba36ba266c56ed44893fddc0ab3037933fd4d174d9de58fa00fc1a239cf44fd3f53775df9b04aff3c3daef9955480fb6b01374499d0b263952c3759f292455f720274fd892755a58d6cccfed87fe7dd9edc9c75a554cd1921b7eab0acd38836558d +cfe5a09a3363e7fcbb97280be6f5bcef49295b3a2b724d8f11e5eadc42e0f58370fcaee7ce6024972e66fa40afaa26c4762281dce1744664aceb4a80bd356a282d42dcb3e8f3ab25ff9c98d7a06e41db8b1ffa1ce4b878b18c9b06bfdb14a0170af5d2895a125314e2f36b8d3b75e5b6af12b1c7ae99e87ff942bf5723993ea5 +dba8a5680de66065714e55eec51efe354ea04fb3e796023739930227f513f003a03d8d0b70280da58867a8cbc25bec56be3ee98c251185d0182c656246380d6088ba4f37b6440eb8c1646d448e34909e17b4c51ec5696058b87ea9182b509a963e5b2f6b6c1ed0b70c99ad3635338b956951c47fedba1f39681e05cd7bd8d7b5 +cec8ffc8e7c03f70b83775d4a2d3eb6c2949fa9b9deef69c1a698d1ea5aa1e090e7a7a35ccc51b160a94a997409ebd82043b0a6ac7461556118b61305ba9006887d4c0b603ac1649e489380ffa7f4972e8fddaa959811aff55e70e34674a5281525b9ae0750aa69d531d73a8ef7e8aabfe373d66a752877a428fca260093cb3d +d90b40ff3961fdc0b9bd0890d9d6ce6f58f45720b6e46c97ebd876e248a054027c40fa9a1a6b123f1d119ffac30f60a836b558bae286b768926d82ba6bdc5c6c115e76975cd2e9f282e165e434229083fc57f2a025e8de756b01ce83f011e7ac82a0c4a995a6efe6878f2ec937b2b642cac433fadbe6bc05d4d94cf1621b9de5 +c51983be4bf08d37e6bbcfcaea856bb66581438a118537014b3812b01cd7b482b8192d03c21c3e15dc61dda8e729353ac22791ecf4d197ddc7d57b1063ff80e0c6dc6570a2088f670f2a401bad33baec2490941b68c72f2c99f3f9ffae478e3f253c4bc2a5d5df040155da0a484421a0aa061fcc12b5178ca4cbc5494383e0bd +eae065f0dd770e127b9747e3f5d89d33c7094ac37ba2ccbc63dfd7d4f2f208d6500ea8e38d6072e6716a4899dbcbccaf0b72b8812e34a699c6605ec008d8eb58a27ca1e1ab0500bce2dd236af669c53469d658cb7681355d172b65f496871b04d16b83c512858b70f4fbbf9557d51397278b13a922031160c1131b10c3c32b55 +d7543c8648c0a7230ec35252d0e9f4ab6d281cae438670a969728f53b5867fc7120110a1be83d4d4a7f95bae092b85bb37adb630db5524747db6bc3f350fda33ea8539e14b31425e3b099b241439efedc44f0c99a1975b6f545dafb4edaea8c395d00c9dd613a1cbf80d755df8a2d614ec5e5e83562d28a6e739b2bebe3c6c8d +ce98fe4c3e7791ddefcd1b9d35275bd2c1f98500ca0e533aa6fdd1752162852a212a60d1278677fc4b2bc2a32a7e67518c1e8f98fffd3e3bbdee1e4999b36f31cafb59ad586796fa40e5d5f96233dd3e8dd5380dbedba349d529d5ffc763f091fb1a1fdc5b012de244fe3517dee4bbb53a5e89717491a689caacee4a21ccf8a5 +e44142ef41cd39b3bec3bc40d7b8979cb700ee9ff6316feeba9c64ea173ea3af565bb7525929f247e13c5e8d7dee841be0f2752a20c1ccaa019dfc26feedd45fbc601cae0f2b6f170be95216ec176ad746df2b1cb3b49d8ceb9ffdb48de1aaa1ca1fc7cdd7cd6f1ad7392f3a1ed5536a82db646c4c029d10c2545d8655697a25 +d24c24ef31e65a1cbb3418e1a091398c92ad8fe1022a13e90d232aeec05e2021651a774d3bf5700139dfc523b4980e9f287d7f2dc5271db70dcf5e3ae6b12b15e057674a13e5c106975d0b98da8edc756c3a038c95d8bac78a0bd9d59b64a5d65ba3d2eba47a729570b9943a74f1d6838f97a461921854c45f0a55afce001865 +f53009a9c7023f570bb32b16c64df08b5e473ad7a4695479ff799aaadcfe15336c2c41d2d9b4436eab9c51a3f8b46f82f151d10d01e7c23f1d2413e166243a60c30aa686a9d79fd7a06694b56e62eab2e9a38334c95cba056a7e354c9efa052fc5abca6113608bea0654857f608366e3637a46ac63da1ac9bcec8575ba391f7d +effdf430fe6cf83e9ab57fa9af31038676bd7efb035fbd4929e305c88c2e86a9d7fc7d1984255b400a4e3ba69b9c039aea84c3bfc539954ea966b174c7e904968cc3594e62caa9d495b379c69f4be4dab289f9560bb82d86d093dc07c828adeeac564c3afc43a91799e74746b6034937f25a7ffd47a2e95f5efcf216fd6e30f5 +fd312c2d37a7f9bc5747c85cce2bb9b3f11faee8eb0cf9eabb5e2b985b6d79c0be0542b83f730f9b0a7d844c96641ca2c646ab162af73ece2b3dd2b5920746083e48e9bc2aeed08b73129d75aa20118a1ec0f4373403c5a82fc1be24a2d53ae8e127bff6fb842d9365746325e1ff6b825bca1b10bcc44fcb7753cd93b4e7da4d +e19bae43e71c102ec61906cfbe7a1c50ff4f517f9500a647a3d138517eaa32df011a944bcb30fb399844cb8b24c4af8e33d893e002efe6d9513e8279a08e2f0d6343387e181cd287459605999280c964b8e4d1eba55c5476dee6970c4405a9872451bdd013ed58cac1d619b88300e50f83922038a96dac937799bc86de1d8fdd +e45af63733bea0ce335617f63969fef91160902dd6ee6208183a5bb57d91d5ea2a1180b90c5b154f7aefcb4ad2b2229455bc2c4c6abf9bdefce1ab45d2692964779cb7461b85c2c81c6e09eab23eeb00a0aa6bff717d3dcdac23d7a1f8fb1dde321227538cb0a22439b81706dcde9aa697694e421d6ac4cec47f7d585eb1b5e5 +c02e43d1b9223510d290d7eb00a1e7836ae45a4930970651850804396627c634be76b272642b4f9eb9732f3f91635761b708cf52d797fabea83cd640f2e4cc85dcf358ad9f58573ce0a663e3a4dc4b35b3a9e4cba2af9eb34886bd67e09249cae43c1c99419fec69368e7be280d31df88022a062eca2915007d3eec666a19555 +d01d138ec72d3cc8be8a256213d130ddc88ac122f45ce1b8816a6bd828a4881daea455646f470dc5af804ca7e8bfe83944e59f3e625c1de06f86fe8867e50444ce019ed0bbf82a020139bd0bf8a4b0558182d5867c07b1081fd831326e7241908c9c83e459459721c5dfbcc85ab9a4bab4c729f30ccf9031ffc7204264c6468d +fd3915bebfdf090a857dba66cd39ad8c087cd94fbd3127cda816cff9764c6b402f3995f642d85bd820961960188619a4c1b3ba044bbd95ce38aa1c9c53a72d2da6cf8098b63e9e98f8f7293999cf7f061cd8d38b8062bba16dc9a8d1a499a02746478c5b670394e855fedb6e315ee1261fd8f18e40ff50d3821f6d9a5add7a2d +f5ac41e9861e7b80125fcc26557f0fa67cc1d1e2d1afff1bb0dd9e19287ff65b21a276021d0ab8958abe508879f38e91b46414c99fad0b6d301bef9dc68540cc0678adde95b7cbd238840ebfd274d4b0853323f19c625c2996bfc1740e88a5d098c7c9d4659321d5f6d04b5d4bc4eeec9e6aa598b4538cf876d5a475f90b142d +d58d247519881bc39adb7e9ccd1b8ab178c43b5858852d84b0f80f071559af43997a191d1f17ecd5ab37e78beb22d954dc469e7d6b457dffc05f0b908cc526c50e36c484bfdd037a8bd59afe13066cd2c93dbaeda276da3cc8a1404ecdeeef6624494decaca9bc2052ad9d5f1a699d739e2f5972e68002fdc38b850e77a56515 +c6877d5d0212ca82b394c8ab47464c59483bf1868ccb2cb1661545659a65eac46aee551b09f1e465b3169d2eb90ed643143e982d394e9dfdccab3a5917ea2b4e631a828aac0caa24bf3324b1de5484844f176975d93bc5a3b4d457dcd39d0aaa5d29ebb4207e03009ac30160faa29795e9f95338614bed64634c1572bb91ba2d +eb4a8bc143dd915e59c99a1469225be57b152e6fb804e948e7acfb6b36b10eabe0383aff8f9fccd65ea336b6c1427fe88bfb88d4b1265842375c8be6db4620eea2b45c9cbcad7db53470e1df13cbc19017951cfb693c4deaeb932450ade0b22fb5f8189ab090278ee1d28504a5483976d28955adbcc71fc18e6544433ff5c035 +d4a71ec4350f51d22b5546c6d7d6d07f1160b41866101ad847c412c39c84ff052d78cd5e2084c4e6e6f85889eb8e3084a6b0a60d9088b42ceeadb7a0bbb9366d5bbf89814c1b7a4045761d0b16778b0b1e9a8ba1b5a6d1139e52019978ff31ad93963f75ed98ba544ba7805437177876b37f2b7560314af5511c2cb43d8fabe5 +e5a5b5a02380762d273a3d808abf781b6409955e69ee510137bed632f18843deebd4d7f81f45913f57b7f1681cc9731eee76ba99f5fd0da8b2b2bacb15dd9ca9df53b5e758f0b8ce323ca7131d1cf675823ebceadf9f9a816eea392adb963557e28c29d966dc24c29147d59afb9db9561aa9cf200ea3a7ef284dbdb9c4b43d75 +e2399769327dd44829b3915999a66e29d023b82e52606001bf07cf1de15b2cb2685431a0167845fbcd06b8e7cfb6ba78dd8aed5edb44bc7c4935e5feb2ab44c1a35bf97dd24967b21b725a39260b32fede553f678c4cf07b0d4a1f23dcf34a75f49f0bc5b10ea517df13cc57aa11d89c00b23142d07ca274238c3dea132e0545 +d08343b82cda4e6743a5750db867698a28d55d3bf763b12b20b5096979be7a8d82dacb50c9cac513d3bf35adf608b1001723cadcec167ed6400014afea8aeac3f4f9e30e8ee46d740ff202c6236a8a85d75b618d291a081e5e145a07174bf2b3099f140cdd5c656670b5ad209b82a6d751a85a91da8c871ed3843647fa5962b5 +f19d08d244ae38b667a3eafc49ecc02c57ed3765a9b6f5e6df373dcaa34d184727e1e7e2d67f2463945ff05b4cdd28251e02fb979b08cf4110cdb4fa5349e74572bbbc661079a631e1fd4978942b7acc46888865b2fc5304d959f9c389eec03bca5a67b8eb9c85fdf381eccae9235e11aa4cb3bd670ef4d0a695a60c9f345bc5 +ea9733f219602bd2f8b57835c0260e5729c33d805e92768558b76ed0c434c1941ea514a0f636bbe6a62a954ff288d61a617958157231b0e51df8e585dac28cfb49260908c10a561c47cfeb6765a383f28e8a2640301e99ff6f22ad8b310938fe9bdfb1dbc1f977936d31ca524228e047b9771d73bf7f1274dd4c8d3c1d8d725d +ccdc84ffdee0c939b3428220f15ffc02333ba05373bf34461403d5087462ca6e33716806c61f68722dc19016bb168f10c0564a6838ba72e90db7a7e53561ef725a4b2d2fd46642b478c7431c0ef4f867ac42ad9adb88c4cb3c49556d4bcd48c553240099f27d91176bef4260e76b33ea684330384d997d8d863c199e5d2fa12d +fc8dbe56db6199481e0e7b536a7b11d1dfc6725f8c9904d20c26ad95f95c45558e2d4925215493052c3a1922d0746777fec9b10564a2a9352fe6367a089545f54cf1ac34204f683907c4c43faf300e42d21a07cbbd91dc6a2e7ec1182a4d43573b71b1a6e6889b24610e873fabaa71f25224eaeba4c431bcb20e196ec7ca2dfd +f8d0fbe2244f8917d1f3a0f276062d4f55386a924b2c43c027155a604e2b13d97c8c02cbf081b36a7014139626a553ba32b44aaab79b4b32206233256c8e7e4c8084bbff1e98e877a42d39b9ae2a27fbb89a374d1ed342864c4de01c15bf7832c1f1a7e367899e7cda988aaa46e8dd199de7bf596312febcb8ee78fb167cd6ad +f521cf6c8ee30acf897098cc12b22b02fb9413ba7b453b09ee30c1a9054ce49913b0be04caec5215668251b09db8beb273ef6cb98a7d832a8262c9fe0008652000dfa184f138b6087d79ddf3addb92a7b5af54f143eef7e3fa9e1e136add0c4259306dc3853b3cb9f55b341389609adc23ab0774ea48f0cd95ff9cd243e12b85 +e9f9e5446698aea25c754a3a109bf6a98d19b9d8b70254afa85c6c5bab62d1ab9db2a5901b8310fbdf00851bc1d57c6f5e5af582f4bfe3a67a961398f259a08f176c1062cac3b919cea6616b1f35319b844cf9a97a47eb166aa5f470b7a5bbeee6b051f69181f9c8d75ab71118f6459f1ba6c49b41f555f3c36ff562f8734fed +cf0da6581432e2b9bd38e4bb9d6497e3e09ddc23915531002cb1006271692ae28bbca7051ead4ebc16471028679b7454879400ba245cb4af1d7b4f5c41ba8278c6450a58f22dca5df7ebe945e67834012d064645dbfef5084f2567abb2a13944ea141e50fa4ed460295d90ef40d0bf5b0a1b2af8e8c4e9d52e34c2f707b2d675 +fc98b4b70d654a1aa86a64aa10a13db39bcb025a0c9dbf572dc1df22ebe96f155df7a1d624e85387e90cf6c763a32bd955dfa0ed99f37ad71aac4f8570d7a0ccf881b6cc4fcb91a62e1cd60488048294fe2dba071bca8689ca63e07771537c7e36deb5f0205a58d041fe6102e81918aeda2b9752b0d136efd49b71a98c7cdd7d +cbfc166895123f56dd9f5f4844b8a1f566082862f6a7cd0b425e1e90f92e129d64217e3f755923ce47572e2e8b1297aedcf3044c263feec5517caef9657698c3b75d6b257ac61dd60da87aca05da46a840c661963f423b5c76e5812741d54a1793f1e16f1d134d3196cfa670ee8d62a691214648cdef7d12db935da44787aa45 +d258d504c6f3d59ccd8dca3ce517279a027fe433d71e16f389852e341deba5eb2c94dcf8714687e7b6a6322ad390adf5e08942568c02a5e586d49011458f2c823d54a859a03e82410dd602ab3b5ad526e77f54b735065fdcdfe6c1657e31878e6d15689b23e1e079175ff683ac53a611a74032269477b5ea00e2a087cfcf2e4d +f611da44eccba932fc27d436d8ee762e3757744e1b2f0edaf8a7c63afe45675a19f695cb1fbb547ab2e3e5d00c804d8efaaea6f60ae35a5e338e5a648927bbf1fb7242898b376e0f66670ae2c0f16d75fe9279dc97015822725d617d09909d04bb4c48d097defeb5c9de928b0747715637dde13b23a37f97483bd87cee7a9f05 +f748f838957c169a4bcd35da0205727f4e036733a6ba5200cdf695c7a46124b6cb39b876354253d7394f9e3460ae9175e82da7771c9a0b2c812d442e66fc49f2c0ef5c4de0175bc32ecf2a072bdc852aac303923d40e0853e0774c7cc7d3eb6ac6bbc63bcefea3da62aee1d630acf23ca7eb4d0923451d19eb2638abb4dc26bd +eae5a4efe1e981d4ed08ae37b694f24b0e6c16c6991f3d88265accfc88803fd7a51498d9f6cfe2bfa5bd4948ef82ecb87da6b1221495cb117f0a5312284ff3678ed7d640ef2ed7b19e348e7a0bd694dc28196781d2475c45d6252ea2ccb443e1860df490867eb607127e2472c222d7bb36abf85aeaede667decae255dc43348d +e8a6152e7462c848d36f38e356c47d268b763542a5665465e4fc00c57a391bf5a735b808af29b9ba29144e93828a9f10df2aa2a198a767aab846614035963d7175d320e7aca496a8e57784857dcc10b571823526bbae7fa16102b1fe174ee3719b0e9f0b1578bfebbc7b77dd3658ade9d1bd6cad35bde306c01f9a43dd168425 +dce3db498d870b98be37fd5908c1a3f9b80372540bb627ab5f3860fdae340512b848d888d31943c45da506683ed373f9dfb078ad441c4a3445bc0b6c290ac4a53fdd53d758503684628ad1722d5d27d306cf22767c3ccb82d6be665622a6c1614e78e4079b3d537276b4ed16dcb2daf1ae2fd3b7301830ca7f18ee10d5578c65 +dc0262673ff106b22fd8f7b0e954504681bff6492f0d76c4813a8beff028377a21696e04a1875ba357e5a21ef720b22c6da618375d5dbb40677629e5d1f197ef880297cfc735eb7c16b1480e7fc437883118c2a17ff7a9adf24415c6fa80c2540756e73c3cb313a1b7fcb58f4ccdd2f481a528ef30c413594146d86e508c596d +c31f4efa0071274e62c6ad18672d29e8d0a34ddd87332c090b19c48140e531ae99fb16c0e66ef1e9258d417dec22be932eee3e8e2d2d1e32f83714f815903b348a52cc583c6376384b81520427f7ec1a93174885da96c8fe5b6f4d2023936fbd216b67f8e0314b6555c38d245acd5aeb83d7b05359f54171f356a9dab4b07715 +cfdcc840aaf8bdd98c4274ace9f46d8036df8ebbf027c22c22887f7804c93d6dc578659abd3d28a6a5f8ad889645c7e2b56057a3666467734183fa59e703fadd01cc2b08909f76a39d06eb3c06975e9b57f67c3517478af4a4457604962eb656920bb228633040bded473f06f055a46045ac54c2c63d109b515e360e5095613d +cd789dbf2f41a8cae05aadf109ead16729ca86da3a303d3b4ac05ccbf016a4f09e18226a6fbd0698c2f9d52fb37baa4b2427278fbe50d64c791a0ecdec22a1b97294f8a21807d82ec09ab91bd3fafc1a79b9e7da27cc7448813975a165ea18771367896046eb9924b358263c279fed1baf36be8bd82ce49de66a304a7807a505 +fb5fcf913fd6acb1fe6a6fa1cfe623631b3eacb0fb1af53b822a2ffb0bbee8cf544bea5ff17cfe897148470a928fa9290b35a487b8db43e2aedf67efe174b71409c8b15e769bff98888f2a69b3fe93d6e958cc0b185ea58ec5ebda005219446eb55d0736ad2ad32cf4cd580e60d25e26f1e792e637c3ac528fbdf1501cd3851d +d7216df66b03f51eaabd1e945db039ee72fbef1ea3f79846d46ab083ca02d74d7068054ac05d5ee5b90ba84c81f9cad3a969945f7d4ba547c8cdf42de47f95542f886d33f1a6ce7172db2fa72b5daac85462027c9d0aa1ed855b7dc1aad537935f30469beaccd3248dc1b8399f8cf627fa548b03a01c33e9d1238681574d937d +c28409f6abdb5ea38bc20f01eec110058cb90a07c1063f69ce8170cf8879ef8679b25965f2c985371fb7b40a2298e8cf61aefc062090ed32dd7477a99f775e39bbbeeb243b4c2b90d1c6bee88647e595e390e3a0a3d2a2890fc1e5fbc1299d63583170fd614ebaaefaf08e9ad323342c9acdf0dbfe2150f89eca4402499ca4f5 +f135258dfa7b82b185b48a111e343b46830ba562a63f53e1ef0a0cedce0d8b6e3e9323c3cceecd577d859f5659e6365085036ca7f15e75fb2fdb5f8d0819c8454f948309b2571237dfa5d08275182d55786efa8a274c5ac6641563bbf115ecf4f8cdf24b5bde89b90490c72a67fd5ff2f61d92015d4d53b5af3c48aee33838dd +dd23c15c71c7be2ed8d55fc98cb7b00269a3ca2f93ce801013fc77b7aab1917bfab2133dea01e0581ffc8481218eb7097f5ceb0167c1f223c387088f2c2d108b06d2bd1ff68e4255f4cf5508a067fa895fa27d9739bf7be72e661fa69163638a04766d8f9d07fabdcf010cf02f421ba893c7ced966627b5c6739bd2e70613fdd +f542781d273191506f5b5db33aae73a25facdde2967d84f1ea53e0fa98d43fbbec42401cbe771f71e1d1a0d8f48680739fbefc1f701546e856cb7bdd57c65f96aa4d686b9af695aac3b6ae92742d8ade5f6f4574aa60773b6c01f533dccc720d9ba285c04013a627fc0a7fb26f0ea48482e94d0f89437ad9a531cba372233145 +ee4e65895d9283d8717a958c088cf3bdb836ed38f4c6eaa9b9051739a1de986781e90ffd268674aceab08e95b2d0d7505619b9fad815876972f3391ce66b8645370c4050cbb8084f97c4eb7ee7dd85d5eeb8cc4eeb6917984a8bcf36f2539fbad5618b6a80e1409e48cdbab7bfef14d628d1028169fb6787b98a38920a558115 +f34ea6ca038a7aeed8f446b56b1ea9a175e082f8a03e0b764bef95a9e255a27454d4018f697c91f76a588e31dae7a9f9eb18925aeb19ddfcfb0b5cf6cfe253c643f3bf541015b3a0fede9629638c49c81650b8ee9cbe1026e04886a689e0033980a3c364069dc03dba3154da56807b325ddd96412960c64e915a5fba57e657f5 +faea3f46d8d6236c5257177d6f27bd2ff7a2312d54512c5cdedaa96effe4810c216e39075136577c91b2e0022f32ab111acb05f8a9b051c9d11fba8fb708b1e8b8154d8f3d6566a52efbfc0d8414b1162b435bd6b409382e1b34fb3d92615b887be1ad050578978012aaa5192a8cc4612d29bda58989bc50b837592ec327318d +c8094fb8e642491e29d3cfb5f0b214255838b38635f637dc8fff89ecfceed440300a2cd3553633d927d88145109973923a89b427cfa4634f77372938355217dbe7c19ef20121b4e213ec86a790ba4e867dd45fe83fa0a51cfb28fcbef983ff0c3c4a3e8dacbd3eb5a8e2b46a571e000435e7d871e8f932d4b56a808ed9a4dabd +c4f4ebe55a4f83579e37ba2779811b10775250d36bb5876ca8cfa896c53c8ff65be9689fd68a8a39281d053e740bebea27fd781f0b3ab5be3f40308d6998cc5a95f1d5cc095e7896825c6e0504139cc5f2438ae85a048daf28a1698538bcab8231a8bf449b1e1815b442be1eb1abd9019d0058b18e118256be7a70293e133b3d +f712ecdb5544ce9d3c146f317d4850dfe9bfa1b45a9d4d19dc9ab459c592274c282edcb33ad398aeb22e6e41b72670a0a4b0373c73c829570a0acb8caf1c7034d46e849a6be96db32e2f99bf0e0382920b6503a8bdc7acaa801c69ac6f0192b9c8293c7bb79f3c5ca82bb3452b3a314419e0b53f72867048710a7e02f4aea76d +fdabbf57b9d9a703eb7f9d698055cac6e6a541a1878222839a7813b84d7cfb75a25eca26df09d2cd42099ebc2269e7ddc60612f5e3fbcfb152036ed3cb83c56a552aff9ebb00a356705f3a4ce9100a805b9ea0527065b48ad0f6655f342fecc35aae1a715a3e00fd02bf177d352923117599d3ff30828a0c57fa650116df7075 +dce3548054466fabb408c58cab550ebfd604967bc785844da99e08a5e03fcc84a973288fcc3cfdcadf7e3d7f0a13d1146189713658c1c32a43eba7a84e7742c1c988acd1aea84acd4ba0dc7d87e3711933289c2effcdde2570684cf3adff3b54f15350ed40af0a1bf0aec47d77148e1937c9762abdf9c7820bb2626d4a6ba985 +f086ad1a65ac4b2afd897d1bb7895ec8ab5477b96d4fe6b06128515843c5584f5b8d3f4b5ae3deecd0719d3a772df47b37a8d938710edc57d5bfce58b7e415b4c57f623f5e50d456736667180b0a08cf0372f696d65f013cdd7a1a2e4dcbdeeb0bb1584ac7383da048cd518ecd9a3b5e7c896311abe96793041de812366fd6cd +c95ea65ce5be0a626740d2fbbd4df61913a5eb682419831b9709912d2d2176ae51f8d23b685eaa15eaa4fe76859ad148f6a2407b1d4461bda13a8ed337e9646259be2000010b2ccabc5b81151f7c59766898659d80144f36c4f4d2dddfb3b72d9c96b1e59e6767b64e046bd6ea612fe45434b68cc7b71bfe3b1936d2873aae2d +e50160abeb3931d7740cb48a3aa0681453c31eb3983a7cabf94cb581ed81ea7b1cca9c2157d562e63cd30e572e25f56aeb3102ebbf7e9e86e1c70da9d7119e7917536b375527fc6ecb2266c1264df2823355c8a7f3201ee8bfff6a1886b5bbd4b05dc78a7425d6236800732d0b7875b59417f0ef62641fb1a48f9b2f4ea92dfd +e4d5215833bed28784a0ad5dd2869b97ae32d0f6ca5f4333e5df45a4135ae61576a83161d03229ad935812fbee75d4f8b5e9e9444a25332855af2265c9107c24a048caee053ddd72b0d3adf16ee669f8f8cf1ae84a65fb947f0843ccf76ac641ff1ac98464cd36d80b243c2ec15cb8a6fad3e45435e503d4144892ebf74774ad +efe9f954c6f3a607b54fd893c9ca2fda5df4614d830053248900f58fd3ecdcc1ad994c55b1739b476ac8bb6d19aac6cfcf987f2fee4d0187461318e6a10fdf3b5936013bd11c582c49f2ad1d09d1ee9460183f45a22aa42c02bc940be4170fbf8e205c87e8c64943b840562ec7db4ae337b1f0f977fde638d8dcea9dbd9f5cb5 +cc4937af80e67e580d30ee54f7948481213053a49c27dc0dfdbdbc5afe79b50f103492daad1cad29667f5b45f3983311f4a3d769fa7e3493e5fd8f9584a3942191104198de7a77e978671a2af406ba6accf1e8ff883166e29ddb00823cd1d4d6689d287bf7d0d511735609668557beafec9d6546bdc3fb631c474eec3e2f26c5 +f935bebb45a3b09d044ee922c36f6a0066e8668b4717c5f21cbc18a36d311caef071995cebb139b39c5077ff3bd5a527f858c545349bf3f50dc0323bd71835e1685a913f98d314c85ec1312ed0ba310187148ae6988497e508e00c0ef1bc72023c91ec8ed528ddf1ec8edce3ead3e88a05a51bb7ebdc1fac29cf596cbaa49c55 +fc26c66972bc7b013f0b879557e341a1ab9b05fba4a5580e9607358674359856e13a1439756614bd6600202cd18f932f737e839fda6427945750345f7070a1e317d391816d58e1500d96188087174df8970cbf8c7c4359da09a143084871079cfe68f5b6470e9eb6b8508ee4b8baa271fd1993203b0f71d074c3bea1b31cd1dd +cee883ffeb32eae7594cfbc23d3cbd5b37c72da6dd7d3d5bfb286f7dbe460ac3b6043f78cadf14715b656dfa92d421958c8acfb43194078759c058565ca181b32c327b94a9086011b5952ebedc80d0e8ad8e66f84ea9d9677e19c2424e122f3c8d987072322f24c5de5ca61b5abc7f0aeffa26972a7a2d241defbaf7af8ede45 +d01046b4fe46abf9b099573273cf61303301689d00de860f94acaa64838d76f25365bed14039eb287e24ddc34a07c183f6a2a81392fba7d6e1d4e0602932ae13339de5a788900a96d58fa25042c7568fbf8939ab65a5e47320b03ddb72091895056d659677ad22ba1ce6088ad515247327ceb49fc5e223d3e828050c5ee8a155 +d1f9a4c8b9d2d6c6ae0d928fd5c2c6fbc78d056c6804223788fbf1f9990298a9fb0ac57d85dfe1d1590c617212d717b292e2dff15279b973f03eca220b76c5cb1b699b49221e91a88b0619384df979f21ef63a5b72ed20c533388ba23d96b6d852490c5837955aed263f56eb862bdb50cbda07c65417b25864bedb6cc6cc39cd +c477431e31e33ad6357203152d48df7537ee7d4343dde4580bfce608d0b07725edbf7e16d5a6ad8ddd79ebff06905225322e7ecf86cf2e9d6cc141caeccdbe4c24babad2da9eb6f696620f056a42579cb5218c1acaccce5a1b3a76dc4110acd74829a31d373825e31cfec03a58102044845d8997b9ed93bd1e5ea96e081d1015 +c29d8fc580e7ce4685181a7c43c82620a6eadff437952b73b655b0683283561f067583447debd62a16b7e55b8dd6d4d2f61a99165006d04576025129c027f2af7dd2d88d7f1faec453d96bb34ad50c4a1726406f2cd50cd45c6cb670cc97d6a0809b556cee10c08759b9b36344aa8627ffb9c67b159664678ebe6b131e124f75 +d64fc2f28b0bbbe44bd528770722849281433f156ca40cc2722d4bc18df19e48181cc7bb7265a68407c8048e95f0aa88706bba6d9b7bc9e78d54b4ac028a96b99813e514dfc032f436ee7d284ca3d4403ba94ca5a93707a20fbc72d0897d3fffbce43e252a7d3e53ab66993cb2e1fa02120c4897dbe444441a63e2f7bcde3a2d +ce6dc18edb75b0a352738d0292c22d924c813c1a9abddc00cdf4592879b566e30d9e98277f96410b2db488a96262e32fc1d104e776db8a10a931f98524fc7fda19b3867910614835fca01a02241ad34dd02719ff37e980df15c16ad99b1321ca7c375eee6569894fa9a07e9f4d7b0b49de1586d2b2243b2c50981d8d1e76ffc5 +c7a9dca1332f1c903d260c67444f62c9cf1d8b53e283199bb0c2eb84a72dc800557709e0d77cbb5ba76b01176406814073b633bd56af3bfc89b8d499480cef7e7355dc78cc58a8a9f11f8221ca79cbf32a25c75f393cf8dce9d45218b1280595f2f496539b6ba9e3972c08c7b9d0732fe9488754a6f4555cabc331dc634dc0b5 +d652d68072085772edc617f0114a69dfec0cf4bd7f7cd50160d9d8852e3672e013522075e2a0fea5f6c0418cb61941ef1ac6bfe3021d68f710a723fff5f423509f75a4b8094dc4f753c791120148ae4d0ae44bb09d09529cc6d29388a5e90cb5df5a3c23ca6f559c72255f8f1db04eb00fa36143a95b8e43bd71172d9363387d +d34134232eb029ceed44cce495cccd5bb84406f5dc027b2901cb3b57f201a619828bb4dd7331f393430a9e1a2c0ba039c4cd8895d5c1c554ec9b48ec5c1ba98d29fb53cbcea1a479afa55f1fc5f00c1d086441af41a833bef4758b7c65c9dd8c2b34cc1fc63c8d558f72d9736c858cf6e182fd4140036e9b590958f580787abd +d95d11a752f12c2d04e41d0bd2813dbea2272e49cd943c72993e38a88f32448105e1cb3816daae65d7486f69b6f7a6f28eef4a5cd5e4ad28b47246895f5b79ad4aa4a73b0328cbdd1999876ed8908d5192ba68bc0a446919081b64bb91b299c021cf2f9609503fa802d86545a831e39a8c4cc775362f73a3c6d1214ebf7c7bfd +e59903fd4b578592a5d6d706264846e17127732a16af4104235850e06e78f22d900d8d427d30b4d6c74dd4d1f90e0abf1ff0f17cc8cfcc9dbf910f90a0518364634fca337aab25faffabec1a7af9f4154e10434acdb0b5d1ee705c198d0f2bca3ae5b32b15b91883e87ece116d6b0bb1afa564d3846715a8b3eb0521ee3bea5d +e90a1f94595c3a7bd75b90b5b6cb021e32090affb36bc094470886dad266cca4c057e48480d92be364d02961b7c88992dd3010fa94f8189be92241beb96bc3a2b0b97e3b0a88060b0e2f17bf0458b64ba357f89a33c6e5c0f0b0905f9eaa1ea5e5d492e4c54c5c4fcbdcc41d546809acdc26e5bb46b53ed95e548ae261db0f15 +c0bf25c067104f867cc0f8e86553c2d46c9dec62a16c5472fc88261d0c557109575afd749dc3cf060b31fe5c25255db5c5b428b46d23c5972e0e3c1897b97876ce743da8ad0ea9ad00a10d0f6b211ef125cf861b17b9ca21bcfced9aef4392c9728cee93b61a02a6725b3fe92804742231a2589a93bb115299cbe571e8188485 +fce11de71e18c85d152046211cf5d57be81b26abf687d094816d5b1bc16e7f446f7f4bd6164db6c7d0f60ad9ee7fcef172517188f1221cff8231936541baad418aa310896ccb0e76986ac6975bf6b5884234e6bf8c719874f6ff1d7b98665396bea1a58d4e413768285d8d1db16dd496b051f3f9e8015ec280eefb911480cb85 +d71ea7196f642b90f8ea627a8f941655f6472d7fc02f4ec24c55cc79f95d661183121e7324628325bd52f33e2f1bc1a14310d7f49930462feee1ce1dcb787af70374a29a7152eca3bfef0138e11e85394335ef7f8b9ac51a0314fdc795791c9ca0b5958c381148b12af682ea6ff5f731d6b3b83cd0cdad9ca4f9d4829575d94d +d7fdca776293f33e09c416d2c1ba70f00f7b47dc1ca6a2c8ac6ccfc9f1fd1e88344f75316a99be099789591600ae779a4c78d4c12f4a0e97388fbd6ca63f437fe1def8f31671ec27f6f9bceec8405932c15da2fe3ffe66c6dc3bd0aeac146867790adc61bfab3da2a0d358594746debd018d2b7d58dbca16f4851269bc806cb5 +ce72cddb8418186abd586c8fa62dfaf121e804090937c323ddfeb139dfed9e570b26e2e28e69ca08ba5515556959b451092c83fa5648fa769122987313415e14bc664402854134518f131f58db527d163db3cd13062a9af658357801535ed5bdf24f97ff18cc824ec9af6dad9a2d3921faf7d84218d1f513ad262f64dd86cadd +d5066137aa3101b001a99b1ee411dc57a6c5ba33dee3279e85d2c62510306a69c924835e21e6541d5c2fa3f74ea4e908024bd299c5f8e6049720e1cacb935f8adab9b4a3e78f83555ea835574122c2cfc9b1c2d14d0e4e56e021e06614d5e76628982cec22995458e6482f7ced45f757716620d27a53935386ecec12b9bc73d5 +dd62d314161fb6c7fbd6942123a8e321835e19c5b3bd6f067dd713106c83a04903508862541e1687d6147954334f2315595e41d307cc17c47a85fb0016fd987c2b6273aef981780aabdb54e6f8eaf269f97af56b8190fa2ae67af1a3a60eb924f0e00e9f0e5345ee4ffa94d10a1058f5b364234e6838e2832c787b673be173cd +e2b1d31e084c148363d4a41c685f15ca460e1ccabfd25dc20b9c51844c17c281fe72c76d2b6d4a093108d5cc88f0d196d24baf65c90af95a905afa7794d9ba873c1fd79fda78b878ee970c8a7a3f434ca44cd35965415b35ebff7c635a5ac74a4500738c86f52db8a7f7c82c4544766bc726f4f2b2a4c02419430bba3cdaaecd +c227213c7f588e4bbcfd2dbf3ac650c6610f03e4c4313ddaf02791a75ae396a25c62089e11e47326e585ba8c5c8e6aa20ac1dc690f32d600c162ea086f5eccff3029d1bc2f590194b7980379680edcdc6f21505ee678057c36245c400277140541d12346f067bc77354fa2abe8dcc26a2df51f0e9b5d05f604e179529d2d226d +c90dde4f6f6f00220fbe1efa9accd0d33f47c2c7f25844d4a3d58964d1a01eb63cf695b4415864b11d25c69cc231cbe4c1b8e8c47bb49715fce7769b865db47c8242882c528765534521de678c800469937a2857ace52f3bfc74aeecdae1b82588c7a3ce44b656bc274ee80c0926985ef828e5d7083486b3df38359285cb8dfd +c3e5c636529c8c0c7a842c434708663d186b95f8cc1a69eb7d39c7e008cd1e192defd05ce9fac89e93e57e64b99ca4801e83e7fffb7f0d5a46505d4d5522ca84f00e05b2e895706ee703b63b897775f5950c8bc5d7e2557d617b55eba35ec36743a198b4cf239d95b9efe62f4c40a8b3930eeb82d07c186d1c3935d3a2229515 +c8b525c6c1d8ae599b7f7841cb55d4ea2af528a77ee21d840b45d644104eff3cf467ea7bf0f0e0bbdc5a81cc4606907712d1235fe3f33551ebd11f6dd54ec3a42d3b6569224d0579bf587cfdba19a2fdd8bc8e30229e0d11cd7536aac5dec9475fee801900f6462114c0b148bfb210c7a27f9b027f1886abe00674fce8ff761d +f59a9242b778b65b309b619dfd19a877bb25a168bf0c323c54b03e3635eb31d5345030dfa96130b157c10b8c8d78f4733c0610f4130e99279840966b0115b31066f6eab749fb930fb844b50265d36d04873a3ab0197893cc62b6703d61e397c8aa3660dd3cadff9e020621b8b90b441ac368f773f94d2d2d4d35afb9ec12a14d +c9d9ec80523510681280ca314ff905ea6256ca3ac6225e478705a662319ec8365c36282f1db47774d85f00cfb3d66aa68cb60a9af761924829e992f0e5058488bfae552fa0c8d847bb4546dd71502d63db7a2b713122455d47912ac37b89d071b272b99af36df64f66f803d3ef980784b0da7ecfa117f75ef52430896f15c3b5 +e979ad6a662fd41f6dbb5ee0d3a8f7a928ae0f0940dc3982ea5a1168a46f5910ccf0d5606193eafda19ae97c5aff2b903b0e3a89eb3c879ce0e13cdf43a2a177c8104d7fc121bb9406a826900da82ad4352f4899d04af3f57a225c86c20a175a96f941c2c5eab721db904330d6bceb8d6a7a913461e389158708c9b7b83d8ecd +ce2c29c73a1a121454c12d84784165325ab05dca980a0390a3cf0af78409cce3e67ab9df103543babc0e35149a0b8b9c677f9c71d37a59eb27811eeca31ff7d568430a434a1c03720541cb69a21947e378d14711f17efcab348fee297e53f169fd5ab195b4be24bcc8c68a9bc00b273aa10a01dad64117cb7615b5fd88d0df75 +c3ec5ee6e08a212829db60b82d68c90e4dce2c6c419dfd7402a1a4ead2c4d7d864d45e72a2b4f41a09a569e4863b2c78c60d071188d927f608f1b2c623aa23b63d8ecc23e253b3c995c85416dc4ee57ff0a63e2e8884f191feb8f16bbb578b5054182b3edd779a0163a873685b927b7d375495571fafb20f420f8ab95c0d0555 +f389ef5bdad020e5dd82a2e0f9dcd3b40fe30af45ac437a727047a934137920df2cde3dd6097bec3a2cf142955651661fbe04cabc3a19ce2d882d9e17a8c18046f598216e3b975f297819bbeab264676e4ca1acd202eed2eb7bea7812238b33988ad4c980dfb1eebc643b5a7b9312969f4adf746844e9d714b2180e6fb19a69d +cb8caf76e9203122c7f3cfccc7d2924ec29cd53ddc0709ee0902237d445c2cec1ca337b1e1ab7b6cb63cc7f5f11e50ae8ceeae9dfe11299c39390db51e8a4186e69a74d0775bdb38d1c2c5f8024ad1810e298285393293d3118edaac82a5d8914c5dff163610db4bc4c09486eaad877840c3ddd16c9098f107aa7614499ecb95 +ca106a4be4fb9b3c203254f44debc87417b64ab4c774f972f12cd1b3adb1c5c0c760e68cb145fbd02c7b682f451ce467f17a0f07b5394e7db5fe209d972e3fd2cb92b583b6446740fdf38e292d18b4818e824f645f86a20eabe722c0f693984b58be5e29a56a40ff2589ef5713b5ab2d7ac803b72936011dd436cd1db6e2d6ad +c69e4a726bdf444c038ae2f146b35ac11f03127abd71686cdf8da7219ec3cc89fcd1eb4a6c83bc6eca90373c8b998be6a05858e52a370b184b3d1a2f4a280f847eb5c252c351f52151e13a83dd7aaa2eceea9361071b13037cb9197d8f7c4055ce4955df497258815f59f2a436392a4c6f97166882189926575682fa328ac98d +f6a6bf9a76484194e7e94c0624ce91045be44e04c8c25bde647d421e6266694a4fcb384699cb98cab4d5e3498edba6c581e868258af1b628557561fb9c5e5e23cfc9effdf4f46a788587a1422a4817152b28d67c7888c9543673c2d8eb3a5a51ee01ff2021d7e9f9eaace73873bcd189f84704f2bd9ad5c38cae6e188a3e4ca5 +d4aeb7fa7499ce369691bf04ffeb312ee235060d0a7d96e2bf6398985cf0b55bed0b8b34a53346adb47826e71b7ba2490af72f9d92a33054e1ad8e203233e356f5de9874525de52e74960948dc6a2dc8c9d1671b463a167ac5c889d5cdc6fb6a087b9949f3a3a4c15e1828979e9bd566f502bf9d12e4735dd1b51ab9ef7dc97d +e1324d35af63b7c5475c19d4642a776c82b69d3dad74278afd80ae38969d6e010f0f78c7a17dbbc505afdb00f05c0371a8b100acbaf7b69b4cbd46cde9cb11ca43d36ecc1d109aac95a7d6abd11f088787d9d8ada4412fdc06c030339a10e1459939bced6b5b872dfbb08e33f0c427fb94e0245613f0b697f483674af9f46395 +e4e6640df3213f25799c6aa0c723c17cb67d707261f3ef74f95bdee8d2acc59af18233286136a9d86610421205424d27f21aaf838b7ee361176c0c89fea4e1b79ee48ea438944746d527b1adacdea3dfefe2dfc5de313876f211cd3f7e3543a5cfa7f07624e913f65c506af956011b854549b6b956c951a803eeaaf6f08e9bc5 +c3c8a60114d2f5d38e14c79b803d2d7af36e285daa5654da567e6feaabaf74d6165304d53bc465c0c72b3f9f7961a996e77e066f37eab45808ef5fc1bbb87cb252307b88eda73baa461b3ac1cc44c37af966ec17755fa66040bd340f2a77a8158c247638008673fd3cf4f98ed552ebdd52b61932b540db5ba90b939f2dd9a585 +e18fab58b61a8b6b28e6626cb74cbf9b1753e51ee3a8093b9283cefe7dd576f2043192fb59a472bb89454b2712ff6bbfd390dff0ae2ecddbdefb80aabe82500c6e4d35fb99b79c6f17adaaae7ef87443189916566d755e8f2f9726bc7f3b1d8253a98877bb5e20d2e8bffff9445cd8cc56af3425ae0901f98494678adfe82e45 +dee8564ce74a01b800b97ef020ae40870571f9d4a4488f9e11bd5ee1acb37310843f065d3b50a5e695104bd7f7fa7b5ece9c5a930c81bdea0c7a140e7e107b6a31597dac446b1a10a8030c9f2de9eb5552b796635e92ec9d98dbd311bb4d18e641d294fdb9dbe3816657cf9980b9fe4bcdd0c1015bcc2053dd1b267ee26b40e5 +e67803f44dfe83b81f73a9b99ef69b758f15a3d108f7eee2ab55e9222ddb611e65de2862bf867fafbf04af4e33e80c922296baf1039d4f76afe2537f988d17e16013cd22dc33e6f9ac8ba04f6f166c44666d572e2515db1de362dd2d45662f5c87b148faa1bef73aeb27fd75123faa6c0ba4aa27dac96c8c08207deec65d32b5 +cf02e0be2c3370c707986317d450090c72ae8e1d3b2f6601a0b2fd62eb7531bf9b883fca519362541d176388699f2ea0434920f11c08d418701bfd0fcac8cd6b23b123551d37d53333d361c5135761f4edf669939f5e9c932525314e623356c155570a6d253f78d6dc0ba1865d94383239921a3b2bedfde0181581dc5b822a7d +df15be4ca79a4659366701c3d33bb1b31a337733b1c5bf32944f501316101d9aa23b0c5426e90b9ce7abb5a489d24900e4004512e51ac7af4aef6a3a6845cc1d82bc4c905d1afede8a0d20f7f38bbd83e9d63eae6dd7d8d69c480661d802890fffc1dddc682fe03ed0241c130e8b2d00a6f8f5f2c99b27c852d6b652a2e8039d +c8c4b2c92bf417257ca6a493b1dfb3acf2d57e49f97a873ae34727d2977d4fc339d36017b08cfdfa4a6a544088ee3034d96eb9c050857eb914a2aebb5acfdf6632361e9604a52559a831db5013fca0eb9381b6c40e421064e45c534f7a0028a3333066491e7e1b35cf6aa82389e47306c7773b745221141744d723c85ea2cc75 +e2acbe5b77c2da5bf137081388a217e53e6212e847903fc2263f6c099a7ffabe54e8c200be0f8c35685088f32d6f4e7067d36913854c064773b4fd6dfd0aa7b56eae581a5c2ad01f25b155bbc88845c005fdcadda96dfc95b821e15889899d2c6a9c35bf5da5ad1c886dd62c25753853fb6c1453f558f40171c33d73ae410975 +f1e94067ccb4c0d8465a57da3e31f3d0a77070bb82f7253a2cc410b8b53b12df6fce41c457e3cf25a86fead441f3019b0220f9b62740dae8a20620843536b0643ded83f214ac93216c93e747b6a74762798038236778fb0e8fb2dfdc90002995a129ed27eaf8fbbee89bd0ddb60f56d30a5778078d4d61dd36ce12afec488bfd +e3e06839335402cc2a140f9b3da84df94187bfe716eb31e4523ae2cafeefffebf62af5e16a379c3184afba15da90895581eca9c4f87f4d671d72c384121a7999200c986b33cf89692d70d171eab1ae7bec2773f243339ece696183d76f6f7b4038e5bd5bb801f7c4c9feb25f3a492f5073efb161564193abe2be7f59f2e1950d +e6eead8428cce555091e3adf44d15d2e312fd5692cd4078930f4820d2cf47c5a040164967b629b0ba1b2d06e16935671f608e1b0d972d070b9006c0a8eb248094b9afbf778c89cfb1f80606ac5413e2e6597065eb0bb0869ee40fca0cabe995b4f6626e6b89eaaea90f5f48004233e845e291386f3d298b46dd5260f618f0f4d +f1985216060ba19e8e0b77afb6f04ec35a7c7cee04f9df6a306b4da8d76ca5c92cbcc7a728bb22ad5b24e6d1eb1f7305fb8a7b41b747747503d0198c38df69bad6c6aab06d55adc79537c6d8e606620b545864e3394dc5c0d1916346cc01988649d186c48dcd1937d21834eb194ed3ef03f2d114770632f4d37a14516cca2bed +f9c9f2bd2342e3c4dbc5894122166fa39625cf16ffcbc84a71ae8145fd6a66e1dcedeb7e6f264209f0c877393f21862d81d2c759546f0fdf8c41708d5362a4f8b6111a3a7f732af1d187ae1ad693117b0d5dabb08f64f9c256153b160d4a957433a7bd780e9956d7e175f15ecc79c03d3fb0698238426c07337202a32771ec4d +cb2d386ba2cde8f38438f2774b54f75a9be753407727da7eddb882beaa0fcb6225d1095a980ef842d06a3847fe772d745b2e615a9720f608fea86281a9e041c38628b4335ae93b446f19a495878d350f9664e3d4b80b153e67a7d1aaa73728477889de9cbb6de0bb7a9537d7327c49c5a99bea50c96c9d53eeeb576010979d0d +e1045e327e0b645b22b8703682d618317e0b96aaf79a8378e6e670c56c6c0075941a48d4301dbf66968588f76b064fcf34a323846b09ec18be65c744d4b3b86583991b8facf99a247fc84078c53a2a0889b573c247b354ddc50be0ed49291762334bb34f5cda0bcea7ce61087bbd967df9c2790f901e9c135847bdf766e3c0d5 +d17d7c04e0e73f0af70e8a681fe57336d20b1132167808e6799fcad00617671dfe513c63c38a1ac28b390a16649574cbe7090d09aea1e9cbb4542ee30532098c4e0f5243491def547f771a6da3d741aec52e6e346d8018ba95d2ed500f0ce37a723d3f99bd8d1f0abdb72af353569817a815f95054ed2bb94a30b96e2e83cc05 +f521ce6d6aa5622862e18019c1b08b71d313a12bbe15f27f6d68cb14e31dd1eae4f1b5b5c85655e685e1885edf7548bab4f9284804cca5a874ba1750a79765e6d74e6024239431045519a5a5a6f9326c5d2bf59711e52785d17cab0ac6c2bc8858fe335e2bd6e0a54a39fce1668c382a3d8f79a48d5bb0ce7bb448f0f46d265d +d27b0a46e492d31872b6d2670921afb3c11964a113bc425a2116008cda9bdee4f2e1f008a96942089755c1a90a80f62c18b3ddec2f22a38971b63f3d9e23af975a8c51357699f7b11598ea7b877e6f0901215d2e8df364ef1ab0106eb0706df83285bf3015f12ca98688c8d919aba4d630516b1211f8d6d8f71cd99d0c599cc5 +ef72eecba638aa1f75b33a9d91981f780b7f5e39371e020c8e468cf72069891f8bddce8eb86075dd459de98a0cd65af719afaee5553fc68b00f8dbf87e33a620bb767d8740b69f45406bc8c3992525c94b3feff1a7d3cd91be83d24d767ef4836d01c62c74ff946ab61c5e5d0964d63c5c42ea56411364d7fc4a4b3dfbc8e495 +dae2195ea4ffc79e7a0b70fbae8d74cf51cfaefa30d15c0e9a054276037a4504ad5efdb692ff97f0c456c3fc66f21f71563aed455a1555fed1d8f7fdfb14a126fd475e582e187e1a8875e25f7959111afe8f141b0bcf641367f672d024a286766d902d7b48de1e40f4943a07adc20866cf2034d32881ff1756ef2a7470886c45 +c393aa8773a783ff411304ad3000ab42d9dcf488f3c4d6098f49db82b339ac6942288861268dbc76afd12c5f9adf3f61ac685d5ddcd80c8a3b639fa59e6a11c425cb4f90b01070ecf9810e5468e8fb2131c807d72dc96528fe3379995c383c36efe88d7f1bf4706fc64f775c4fa9dbaa738ccad52004540d134b51f912436d35 +fe6c3ab23ca3b1624a56147831d8ce58edc77ae99d6db6f17f889bb19f8b87ec488ecc4d4370a26978a553b5769f5803bf3b14893ef6f779dcd4a2a0a6821ad61e63a4c8a41a58ad3c1c5c62e72f524cb63992cf6100b8e7e8a1f9666d932f4f29724a3645cd20727a615161a08af2689749bcc601f37912971778b7d738c62d +f87d31fbc3fe86c9f4e6f2d6e66044b0b3cb461ec9b00c52747b9b7cbd1bef105ab9cfca392e807db5ff40103aad29641f1d0408eb4df587c8c44ff78729db10fcd76f56a475b3f16bd28a7497a939ae2afb8ac23a1e89ac29836dd5bfef509f23796a4038c899bc0ae2428cc3fc34564f4a6841ae920a50f838000039751d8d +dcdeec99b18f7d837ff301714ac199acb2d8c3e73065562d6635f6644a72693bb098d56865fbcb921eaa8c417719c634b7606cc0628e740f051677c920fe386d546aacf0de236bacc0fe221275566ad92250387c10cffd555d554fedf9ad3f7c4b6cda017a7e0b8040505483cc64285305438da2d50442295e4d535a8f9be7e5 +e786b2c5dc7d1439be99339d198b15b98cfc3f718adcc65bef4fbb86be6dc7bae3dd12734960659f4ae9ad6a45c66e0eaa6efac0edf6c0ba72910e1910e9056031b3f6c58538c0b24d5c325d7c1d50a78479f7c7f3e9cef501171ccf45f455e71e91b6f49f4362d0fbf8d8855da8574d888fe26398160323a45668f8ae62e9e5 +d960b0d7131cc252542f29a686a2e1b9b61bc5ed2da6208a6a13b461b4cd6606505b4d5e640dea6e11afcfc5caaee218e78d2026a5b44bdbf3c8a77f789ea841f52dcf2fb86b0078bc643341856b1d2fe18441247e58d03065f96d10e85620a24adfaab437b5ce2044f900239d868abfd09ea56434986a2a3da0e34fa1f59f85 +e9f06ff9af989968099c7585ee539702a91b9966cc1c0f6fb7c7a20f32f37b12697f6afbf0b27e16187893bd40cc24f53aa4265c2195a0f08ddc17f755c4e3c76a79d1153e6a2d99bb9ced184b4f031e7dffff14ccea6b87a30dba589ea8f65970af537297e40c17a94a4aa4504f03efbdaf05b38e6015a1102235897b4bfcdd +d76a70ac8966f4c7f4c87b96879541d573b2806996fb6a8a2054d8b56fa5a19e318631afa4e83b18aeeba714eb1670d95dee8760b9ed0f5ff17b8bb75540b5011405ae8a2da96d87cfab2626983de17a890e424a25b41401b990127d0cc6b1bd786cb2786fbe8080eb18e98558894c3fe383c7bcfe897bf98dfe95521fc12e7d +f7bd785aafaf4f2bdafbf38235f1e4c730e9ab5dc4fa3cdc9b9ca90b06dd9bf8f382ed58c7b0e34cd51c2cfe7b59345aa10839f3345cdab897ab7f553673e0bbf69ef0577ffa2b758fd2a6decb7a24d9818227aade82801f62c453b95e3087d8992b435e50e01f83a4833967a8b75ce37a760fbb6cae3e99bf89130e1736755d +cf26ba352f678c227c25e5147ea8de4bfc4310d388530d9bd3d0dbe0e9254fea8483ae00f5f0ec81dec0e42f5d1bf948b8519ce593737e318925de8c00c0214247abfcdbbc77df57d750b3a9c160b39ad9260297f7fc67ea2043d190bf0765a4fab4c7e6edfc3f08a26311de00e47b879b338097574e4a377e2387ee9c525085 +c6064c4add843be7a9bcf2582ced8aeb459ba930241002cf0325da5a2d2e1b5921479e50b688b069fb82929469ffedfa5a1180f7a9a6168168a64ef917c909a7f4ede94357203f1113d0c6d2376a5e39d37482511a67ffb65832a4daaed93f86ee9739b42c62368eeec1434d6c42a1fe53c5068bbd3ff89549029410740cc23d +ed15402f355404533c4592b42d93de22ac89ea83acdd01bc6b742ce77761a4743fdf9a069965a7d19e86218f8976580be43e4a1d978e0722668b060f95c0365fd9c84f732ea445436040c8ded47a88ed05172d17b46e92d26ad56afc99d0ae216003413458b2718cc194e559c4f141e593996bd9888d57781b5aa33ff1b44e95 +e83a24b27cc273db2414f5adc3d70f5b9926dedf39b4118298c908a26fdc0817341485b2c847ee98ad34afd0a316d32c03c455474f73261bd3231e08c09de84c051d1efdcfac30ebe0da967755db47b13928966f6d0cf348e24f6831a30942c3b971e74fd33a1c6a0997a4db9eb486aff9da54af901149106b991566d025ed3d +c76da67e64ab8b60d859753dd47d501e96d45772eb72cfe226dfd6a52d026034f4ac2cd8f4113ca8cb31b830eec8037f94e62273653608152de1fb580f247e919aa25dd58540902ecb868f5e25ab6deac6caa4dcd48dc9f35cf1939f1f22369f98e50463fbe9e368dff83cf27847e2ae45433c747e848d995fd26d68c9fa541d +fcc228f8f74fcc29e4d70f6e437d258e34a9db8d1af38d951eca478a66e56a776423b54d636b7537f46e6b1ed1d8e19eb03c865dcdd1090db77c1da5a695569f4f0b85d554ab61c8f65d04627da559d43f5b44962d38bf3ee742b9bcfa2db3e9120223b31617a631b80b1bec2cd7d1837e452d6307c4c1c7340193075fb2b8f5 +e076473f86fbe36192153f0864ea831bf5d4dd8cde7ed5d7ac0dcedc4cd36e10edabaf12ac16e9978c12c60506de560c18e3cc85ef2f5563bcd434f7c16413470f9dadc62f92b5b01328bafd9d7a2e5e11db723a8002eabc1d116593ff975a3076c679e3b56734873d51ef99fff20a2f9bf4f06665e5e84852f7ccd830004785 +cf9e7ba49264fc3070173fce670ff6ff6f11935237a557b68fadce974bfdec70b65822bf865394873da79de16f4e506d244477ec4ccdd1851e2cd2713a0075835fa59acbbee79d2439e4d1656328f339be18605976317882fba6b83dd21fee67c38217132eb115801ef009558886804327c446ba577dc93a92ad71b2888b825d +e8ac898dec76b876eaca1dd3d5b73ecc8a0c06e04c15608b1cf9cd20917d3d72135dcb29e03c59dffce2c2e77d345c8232c32ed8f5ee58d08ce2396d4866b3b09611f9d7e71a3eccdf46e17d84c53edc861575242f6301802fd237359ee6961bc32ae74fe34cf64bc0513cf9635d4f046d229550674dea3dfb7e7dd7a5a4441d +dead27581a5bdfc0ae2111f2bbd3a2c6c0bb996dffa395b4b52b9c30cded05e6a363d314ee9ff024651dffa44aa3ff0beaeb736b1688d9d40fe3e0fef53d4ba6c78446306d6ab6d014af2381df992a526ed354aba7f3fc20e1edbca83012e40566e121801018ee1ef32ee9687ae896b9aa2f3ba935f199c5eeb7953a5eed518d +ff751130a3aa372c26cad01df7083187cf5f6b1604a98f63687846634c3a6676515b839e49454eb344eb894dd4d9b7ac4de7bd8b8de3aaa103b45124e2477e17c0a44bb32f4afeb2522391fce1ba6f89f5596ab338a8cfb1b64848d517b96b5daa4c164ca5275ad8adbef3fba0f0115303162690de9f75aeaef3cb2a831c98cd +dc93f8f672332db4d675cf638d5c3a9b9ab7e28c011802aa30ad61846ee175916a5a9daa13ce7db426209cb409fec83f26f9f4a653a1bfa1c3e4de7318bfd51c182177bcce23e120c601d67f21b948060fb521f1a9cf4a5ff5a260af17dfceca586674084a012c3156e3c59b346707d6511b7f4cb668fad7e517e247750b98e5 +c82bf0ea27c2be5b5b90850621a508016f821cec4da9dabc473fbcbf0cff53e3c13fa3ceea1c851f638ca622d30dcee8519b30e54bfe7f2021dfe08c56a03323453436007a1a0d5058661d0e83ae6e746e27a2db8bc0390eb94a45fb9b3b6ff21f0874805b748eece85a21675b1bf0f87393eb64fb480e776f209dcdc288218d +cb01ab004a455102d265024066c28efbf293b3b0b8c5b97a2adafa84c2f0ee25c567d46dcdc4efae7039917277646f2baaf507ff8bed41e88aedadec96a4cad46b77af2c7ad24bad0cee415ef7fe59c5459fce5334537265db060fa1fef9526d21d4dc4a932c3d27f018b988c51fcc54b3d45757aa1e782e43f46e6630ab7525 +c8fb98ad46a049845c83dccb2c7e31b8b16ea956b9b7f30422ff2f53da87733b60c801751ab394d86449a8270f0df4fd25193d886b8ec7c2f2a4649f2fc39640e8634155ecc8a1a191f2688c1f5adb42843c9d7c73f7379082ba202d30b8ef70a397a35d26e9b5e49d4e3cd87dd6b051a642efcbef58b9b5cd05f349fa724ea5 +e154fd4f5be7bfbeced17aa18baba53f19837f0c7964be61154aa49c6521c9f38d7619877850275b9573cea6e4114fbafd3db7ccf9bd2ee023821042a5e6d8b242f9f62a7bb601d1118573a628bdf8500cec45235d7fec639b5795af057c863f1838fa15eb25ede388c5f7ef53eb9143aac8aca8d19efa3dca4bda97feec5e1d +cdeb910095d2e9345ccc30254efc1fa238ce11958ed30d24617110b02c18136b832293835d0963910d43d8247b9a7b19c49fb479b1717f4ac7b55a2888144aa4fa7360f9847fdacafc98097e7f0b3468eaf0ae8966fb279f1def69640794a221123168a021f85d8d79ea5b1702c4123369d598a4801eaa973dee2741d915e6ad +f8438935ba59e7c95287e5c2ef5d2950e736f74e6fd622f034c28beb3184cd9c0f62cae993bd4c1fcce3ddd5dd0b8eefe9fde9165211685907b55f96833ec32b3398a4377786ffc5b3b1222345b488662802bf89ed7a5b55edc639b9f941292336c51874ca037d33cbe951cfdbbe876f5fc995075cc4ba44e3f6cbdfcc59deed +f37918b706b8ceba15db3e18cde5d888270e3e01c5d5d10b5606f74047587671ed3c5dd0cabaacc3091a82786dc9c17956eaa129196d8b007b4f1540c520fa8c75990c7b63603a56a3200a1ce9379fb536cc2216c87d850085145e230c959f1ded675adcd029fd57e32c762de466b9921470b357ad945d12871bd9a3f25ebcd5 +c5d4d9b311fcc1f9d2d2dbfedc09c347d5d793746335a7f8db899cb6cea019cc66bb50afda64b84c2cf10ea5b72669c598924b1c9735c7ba423bf07c06158372f514e687f5284979231805874905cba2bce044615ebafeb93ceb672b6527c20f48b2917b1593c1788506b75df278881fc77a7c0606093bf65c15156daa594d95 +e63759f9d2559b33c4620d6c598532593b957d232b873ea35125cf190a1fb78f3a959a100d4892ce47b87c84748e1a5b232cdeb3e404fd6cbf19069f2c8de134458854ed4e07d95131af6c441305ec7a70a96058a2882af2a342a626430725a2adb448b7bde4f4adc0bb275e33b44a97d2e9329194f1c815ff57428a1e2cfdbd +fb6dd7d761b3a07b56cee7abf128faabe4ffe20dea8169b2cf74d3dbffb159d3e085cafa59b779433e89eca22895833c491d2409b1403a4d6d437c405848627065dfb0a67ebda9458e474abc9aad869ffef2399abba08185eba0f7ed58822224a981e28b0e7314770357de626418235987b194e8fd6dcfdd03194be656fd60cd +c204ef8a6c39a3f23133957bce1fae1158b587cbbd73797bbbd6e37aa86d747d0c8793ca59b658e9095602aa882f34cdb9a9d290ceaafd9d2116a15f6cceec75b0a150cd215d2e9b993c5ed17fc829be4126afcc9d85851e5d366d18b47fe90edb38e72262dbcac4bb701128c8a2d942b0f110c41d352578c323c1cd7f8ca99d +efdaab310cd92e5a7dd0dd4f5d387921fe5e18b9358d1f8d4178e7522f2aa1936f9bdd5de36ed955530c65f059aff8d177da54b57281266f188309a037533d346788175022f8a4995692294dc2a09b7ae1e79394b8cd9c69033ef8d4d07506fd0dc14967649456b8afbcf480bbbf1c6bb7d2bb0809c88c0f8934fe89b572d9ad +e3f62e0b73cb17ec33426c5a4ba8f4292e03e677f5d1617d938e93e54304f7819a3921ce0df73423902947ea2b4dd162517380dd48ce63f30e8b7df9d7194e75564e72b6809056d6db1bfa62b3dca60f7a017291f8742477efc7eae8c9e7627843726fa4a9fe78dddba02d9814844c6e8c54febd3e2158971fd4779f725f68ed +c3c9197fd49de1a9aaf1ba03302a0849278a6bb8460afaf38952d542cf7afe6f8c1fc40b7240ded7a7ffcb5b23e4a5b1507ff7400db4d19fe433524474e21f9673c88c098f9d033c191809e20617dfd5f92cbcba5c8dcfd6652551945d1eda48f6dcddd16ffc05dbe4e3cdb163de9eac0726b7dd8ca388adbe54858471319325 +dc87423787fb3776d3a38c010062304d7fe6f8e4bc5fe8e66201d9bfd724b601c5184ac3267ddc8cddf3880542fb91ac077cfd2b7a07b49b90a550f8445c1c68c084a77321c0d03d005c0e666a9ef3709c1708e8d5f5f2d82f0ec81a2de2fec456b22ddc5243b2ab19f64a74bba5fa6c2a091258c5916c2c49d2ea7f0a5d084d +d0012d2f905082a4516b64dbadf6a52659fb6065fa31f6678c00941eaff3d1e2423a57d9450fb53dbc956536eb00844326cbba017727d3703cb3d83d7e30510a25a0e3a030e4083c747a9a33cd8d8ad28d2b249ead1286cf1de4015c6022248801fcac6320d2b3aa398b7c4ece75a6a9d9fc19d460c657b9d9aad6bf7501a5d5 +cfa3a9a0b42aee7cbf76b882f2ef49bbb53f2eca837a6660714545c6a5988a819a82b3ff72f96c828f385b34fad06e52fbfc3dcc6e43ee9dafbefbff3f5111fbaf795e0675047a21a64c717b725d027b1df53e680d49e2a0fa68975b8d616790535e6da217b58ee3135043b13f96d9d9f14ceba25c80e698a8b6e79247aefa45 +deccd1e4355492e13408d4a9ef52ede6f736c3bb92c85bf76f264a053308a228a547fcc55b2b18c52615030aa71bc0118f259e8638656f0f912352d44ea4964ee652ad4a277d02a96cc6128655cd3caa276eb2622de7a6dac84bda2300ba6ed68d5883db8f45d88feaf67fd7af568c09dad637d46cca9dd7164913bc21dead4d +f05f8e610335c370350f81e7d3b8681bda7c0e51293550553b028ae6937115061873b575b342c8e0eb16e4a94a7263d405fee689dab1e304651abbf9dcaad9cdc3e0b78a78d8d08ac7bb3bdd54bce8609b4a741d10540e88402f74fd20a931b098f83e08efbf73ea2cd91adf599a06e3181f369cae318a388bad70258959e5fd +f40bd808d8983b526dd32870db7481b0026292b56b3fb937f2ca04a20063470f46f6903dc5c323dde933fc6df412d04d326dd3f2c3c63cabf2796b3a414d953f2e164fb4d7120cfb555ab5499f097ece48afe978a71c8a43deaecd680f181087b380da2186afe81e60470daba27cdda8f72c1e2013743ebb8c7c7836ba0d9a8d +e0a5aa6ff2ed1f0a13e9f226b1d3e1daa1dff2a82fbc24c57f4364ffb58fa6dfc3c146aa2a0c86fbddcfa6255398f4d5309e5116b1e05e044ad396c780312b36c56065218bad573ad88fe687502ff51a6490c10b3afc0db2faed8d282fd91f6245ef83361aee9485da9d7cfc4ef91c9d98d6ae5cf383d8513621290f3dc4d42d +ddecac7bf804349baad7591ee4130b79bc255a87ce3b3af695c7ead8676ddf607832ea50bf24952f57c1e19ace029361d94e6891aed1dc095d784e4147422676e52967436cc963af0914f335a7282cd7b73188996c01113d78ff465b609608faf2d1356ec58044f8b9261cd09cb3b8449805ff34abdd257479dfb7e699b9d485 +ed3e5e09d1a52ce643f484d223b312b47511c0b5fda60bf8081b69a6470f94713661b098f744fd144c1807d7bcb30117813d4d6f506d232c216cd0cf81edb59180ba2bf74ddec8f7fc1bc720059253b36ddb4758fd39986a097cd636c80acd11529b24e9dec6d803e87cf990cd6add1c6d5f16cffd74896811d05d324bff511d +f70b3ad2f71f7561b3afb043fd85a7f703416a0571aaeba91b098d198af0d19675245b53c0b9839b8a3fd4ef37b95d2361809cf72f0d057654ce018c9e9c9222419e636ad7b7b28ab9d0deeecc369869be70a511c15fd40e8e9c35858e04db42e6d08614d2e98754d85206ecce8121305400c3a7eb1be090406449bb100efbb5 +c3cf52f73bb03e184af1ee941c56e4e3e4280b78e2d7b3e9beef82f896fa241d58b5a260b8289708c88ffad26a3e907da16bdea925de7c61de15b0330f717b05693423ed0979130ddc3771d424839262f4135e270b396a899e40ca2f799a7ffee07e65ed428b4a5069987e010754b3455456c28183c46ec789095938d17be055 +c7e0b0844d2d1a7a638dd86b30ad2bd20caad56264c4ebd10566d876097c09785110d4238146ee0a1a219c9247a61b4e39fd342546946af09b9820bcebef102efd8be1b51902acaaa5509869b9ebc5510aa656261313b7ec6facc4eacadef66e758d976ebd613a1b42f0ee54a1fce38fd0096887427f61e007f9386f576b9dcd +d4149cbd1f05bec65413f244f06bcd0ee3405e734d57cd023569e58f8da93fda5dd2cd38c2ad1ce0ba131e9fe0d5c3e898699493276431ace3adeef0d8d74c0197144540adca344003f77a92dbd168e0320adfde66701df5c71bf439413a61693ff137447fde0cdba216e33c78b73e1c6ed6fc813b541041728688edf87dfe95 +dedd0cbbba3cf035e2aaf743b0328e04709b0a27089553039a37c46fc62664f4c3d68115e470517aa546cf3126a2771812ce0c0c2631f435f25966ca88e37eb49faeb9d345618de4403aaf0f7af33c5044e806f423b0eb9180f6ec797dd4374a08d3ef625377787203c5267c8f9014d32b575d124047da743681ad677303334d +fc1208a5ba359665264d35fc6bdc4b23a187ab3fd587a2cab5a3911b708c68dddd57cb150ae32cb3262393e24df810c38329a679bb452ab0eb7a70f7e679285f75b83dcf470390537e02cc0c83cdc6b45c0773589b43b144797ddb6336a8103365d09dbb1b5edc2e5d568079234cf7bec794e952b8f29944ad66e049d2c632f5 +dddfbe3908d0c1ab4ba1c000a2a154671f440d059da2d700a6eb8a46f43c8dd870f352b74e7c96aa4d12e7c7ad5793d3adfbaf242d1b48b32f7f51743f6222ad8f7b7dc663b7e43dbc4a07ad3be5694744e9971d156afaf090053fc24a26df63a664728c0c844b95fd2b46afb22f4d8c6e97b63bb0974bffc3bd146ebddd1fd5 +e0efae147c20338a15d2fb5a5196b402cb9536cdefadde4cc9427f5308a7693768461575c1bceab20e9e28af8f5ba5fd2c981065650f4adf4de6a460c29c395130007d623ca2409a9badd4e3bc00a1188fe3a19005eae44cd36c201126ce72e229b277b220959b76ca71005bb246bab998937818c238e8dd778b0058c28a7af5 +eeda184c6f8397794c654db95158c29a0957b5d273d1d44432e2d212e358592226a00b284670c7182cd64899398bb9f19e4b987ba7ab08fa09d8fb41504bfaca985f1da13792f5b44066c36d0036aa4d48b35ebb6ea6cc173928ab9cfeaf22a37b540b39f7cadbd66e91fccfeb084d2009e3eb3e57885cfd00ffc0ffcc8b2695 +d85227d4f2889dd7f1e39c15060490dd7bc2ccd84f4f22179c74f9240670b68a3a0a2ca2bcaf3ab7512597db68d794f20313ab1966704897b32fd993cc8a656467c66dfb00d50991c01ca979cc35ab7aa0ca463b31829a852f51ece4076fb8fab7d395445897004d3f4d6fe07cb4f9fa7d6bfda3bef6c1d0c4169339da897575 +fa6393cb5800304597bcf745fca14d20d93de2c1f99f3909511dd8b9ba76072e55aca206b562721095499a302f2c77f7ed31dfd4b5011156ffa6e9f96b45bfa1aa698a863d342561b2aace1414171e8280d01e902eea9b2c476465628168592550ba5204609a5b7026d86e85b660739beb085c29f7d9114df91bbd25d59a5175 +e2b832d391a032e7fad9bfd592bd8dd27676e2d64a7f237b75c3d33e6a58adb73cc536681969d88e768ea287064d87de15f93dfafbe1cf0fbf80403793b739ce6aba95b73312b419e31613c2cdbafa65f34393b14ca52ef3ad565e5ab28e4b12bc38199250162992cd29ee558f2fc41f448ffdff6d34e3e259a413b22b7dc405 +f9b8c2242b699145f93f38e0d537eea01e147321aa2780c0017dcf4db6d2b6157964c62c5ee1f16630e3c0c5dfbd2d79fc68153875f2d5231f64537876d7a2d353cd70802c227655d7afd42dd94ccd79dd123f86de8a524c96daacf0365d4bfcb3b255e4598fc119938b151fc7ebd4b6fe6188298ab65853acaedc18f1f580d5 +ceaf59a8150e25e680974300c03087e734c24de34a838035f6af22283da5bcd9c8c76f20a049d26c85efe92928ed12d2c8533d139a867b380697afde7376c8f78593e5b264b224777d525d4d0a62b39d4fee7c4c57d1b133bf181f7c5b3805b727ba0630cf28d48d50e55a05c6735bdd9e90e4b98767aacd8cca543d92239cdd +ff090e80f5837f43f9f393a2cbbb87657185cd70b0b7c7ad681480aa48e8f5d69773316c3d6a9d9af1c8a3ce9aed950484a5a77849076650d573de967a3e9eeff5f70c3230014e39b1b8ac200b8618c201d83893185c07e75befc0ab6791cec4f0523382b1047bd40296642135d9498715f516693c624c0ddfe0bc67506a59fd +ee12ade07eb483a078532f5940b71a9c71b2310d2d49b56df3b7d2394f75d1495be2ccac175a52b9228440dcdfae49759e38b5af2de3563230cb19c06acd0570fa3635548770ded37e0b0f42293e978d1ee0b26b8c105c78e3ae12de17f389eee748aa8acf91814043342df26391c9d9e90dd117198e65ecf9c1d1f5216dc9b5 +f33b2b58ee5e248e076301e4d4b77ab9c9d702924f95f13b486e272da16fc9f57fba60091c79a83169fc8f08082875e12acfde59d10c8ce9944bbe8e8876b31645e074d501c146c651fba3ace691ebc0eb18c57fe7abb0b1efa3b5a497ee7899ef95aadd867f9a988cc28d9d01a163ee0dce6782ae0a45409b6a104da6805865 +c15736b1ca11e133c090d5a065b53cd82e2979f44cb93bba041735f4fd339955f1e9f2a753f7100c8aa7d0df40b908a74a4419999c0f4736021aa171c3bdb5974aea4ba1db3ede0b9ec3ee0eee5789dc24a6ae4bda8c5559b148648b55e3a6e2412970d9c2b0fe767fad196b130fc88ef383f5511000af8b3f74c2cacf165b1d +f16569ecf0eaba1f622c8caf1632b24735afd2684f3a7ac86d8ec679c6b7fe9212ef216d37c210d3ca5849c167ce063d006551d85e2d40fcc12265a14467d8d36a67a9972b7b72ff814f90bdb473f0c8618a22023e80e66d6e0df8e466ea995fef20d4127fc3d690e803e4ff62ac8f88f7b384a7a219fa8b116269152c2f8d25 +e70c3cb7b95c703d9f9e4e5b9dd6260f3a4cd3deecb2b7632652a013158fbf0f5e4b76287410cb20b627737fbf7fa92f85081a13a00a3fc59daca503576d8b449b600f6d9c57dbb7c674309bb1c4af7fcddd90085b68713ec20c766c442a0ef136731101bcc6b1b9633ca344a590ec7b1ca611c6b39c161a4b8b3016ee7c615d +e94d5b4afc86cef446b4a4b0004e08f5b9b94923f907fb015e4959406d03a8acc13a51d825b3930d5166ae0054622aff440eb6f2550d51e7ddcc88dff5dc3a8f8120cf6c4a2f3e01902bd8d86c28df484e5b776d31d149a47a5cc842220a608eb0ffa175eee3eb3280ef12c5b2a05025537abe234717f6e2aa849e675fd4aab5 +f8d4915ed06d22f36675aa84359e1a877b369a6e556371b59690ced0f4d592860a78c49c6bdc8e7ea1a1bd1109dd8878d53bec463b3ea10801181ddd6257daf65aec29f188a706f0039003f436a2086922c7c1de0f251d6749855b0348df64308e0a0ca1fd0b7723dd4f35956662fe0aafd3d420ab19c5487756eedf86cf84ad +f35dc4c3be8a22c7466e1d8e9b44adf3e91601eede366fb9658446491715ea34524d76d8660ddf95f6c512b34d52c3cc4e6bed657cb6e40347d9cc46dc5404cc2e54dd8a370041ff1651e20a8084e45b7b73334ae12c19b5885fc8c626e9937dda3c673365f1c3fd785738c20c5723eb16e604f2a94c4ef966f577262c9e5575 +f265c490a6cd394199083e842612145ae5576ab26719701f92d9e084075d66f4f8eac3a6375abfdfe3197a79d45d30e1a6b05b2f09c5d884d495b1deeaaf3b5fae36541290093078308f0e003b9b271de921051bd13216bc16c996c1d73b3563a4a1e4c71bfb1ec962b10efddbf17c69b77621390a9922d63952cdd4b203a2b5 +f5727c0c1826e112487441c855f1225f8096dd90d13f152a49a737445d03c78115f963f88446f2c7a3335a371ad886150694cc476a0ae0ffc5adcc6368190813938f0744baf6e4def9a211afbcb3cd176166f3ffb23a48a793aaffa08384979d4482435d3126a5ede1665beae8426e0d0c12ad3c0077dbabca8d55ee87b8c5c5 +d793b9f288bd065b38d4e80160a7680af407bea4c05b90f40eaa8736a6a9bf41b2206f663b24205115c953f68cf2be1a2bc7073e17d8ebbf6897c0caa9480192c014f15a9ccf024caa11ef56fc35ca06870d8b86c41baa2fdf6ca00475dc3091d50fa452021e66db6cd58e8002c37b7b43975f23373ec4840ddf8c5afe3882bd +ef2744764125c14bd0109d542b600ff2c445c3496a21409b7ecb9dc0aee0667b564ed41d819537fdbdcd4781121e20c151cdb342facfb9ceb1fa39542b953d5db58ccd7bd5ad36e0252dede437fbe819312c6ebb9745bb0ffdd84f99b34ef0d323b89f12abcbc2e90649ca09372e226ee7009d2600109967ca0b55315062aa9d +cf3cba2e0eec4502ab476834d2ed47764c44d45c750ac23115f49c52ae41ca82f36930c92e424b8daedccbe1cd905068e43ff1dae15093217de2e42eaee6f113ede1e967677bf41b8de614329b9853d7725da58134bcbe055a73827bae503b15f7e1487e453b9252c34006e20127fbfbfe2a4c3d74ce558bfc1734e4728edb7d +c9f921d0852f020ba370b4f2d58dac49176eda353b7db9dcecfd73c1465d8c7a1f4dbede864d6fe320d8e5a2386cbda7ee51d9a0efa099b99b1128cc0ce959a1938fbb85ac4c0306068ccbaea4bac0f7ffb2adecf215237e23e7397b949bfa852c38ef97d40071780b379ef1747ef0be6ac7951f1499ae383c43b86cd34658dd +e49e960ab120ed0d6f3c980febfb5cedc0076a4f3ae7f3b5e2cf9f9ca783a390fd77b674d6c73a9ded0fc3f6dbb1bc91242780ba1f4edcd8098f207aec2ff3bacc6694b6bcb94efef3df81a99c4db1d89a7d3981671d0f2f62a4b1900c0bca3aa690171d04a927183d381e6c67fcd7147660dd43a554aeeeb737f3a5cbf1d635 +f2f9ec42a76b676a5951bf735c3398d8bfde2ef02cf24200b6270745316de9fd949c489fe43f1b4b6212903931e29bfaaf77fc0ec9b0dfc7177dc23fcca35f2a0cdaf82d8fe53c792854322f98c538b968efd86d805ba173e23dac68c2cd0e56796959a5ce5bcd195f89a45ab5e371b0d76b92b348c5e461fab00570740a2355 +fb2cc865e608517979f6f4ee62be241fa5a43f326ceac1cd90e6389bf8046a03bf5bece2b7d3772dc0babcbfeed93c86e5f712350bf429410509a680e4ae335a0893463c423e84cd4ec649bccb376d84b67699f84c8a7471ee4ebf7b5341495be4aade3f16181e21ea1624aa8fa48f266e3f358c5527e89cc2973d9d02b08d85 +eb853c389114d97737af83290fbfa382ac883d68dc89c47e38b5b252a23a61a29a5709aa98333c9f0868f72a3ea5999f4d7a853dd8f3ed18e1acbc0215c97ea23e2154343338d3451d4c5f656eec491f9c2f2c02d69d760be048ab96937bc9a6bd60709066bada1dd197c0c028d87367e0c02fc4a8f19468734a96f3a94eac0d +c70e66e78b8c832a330525acf00fb556c3fb9902e879952ba4f5ef4238248b61d9bd5a15f9346dc23b3cf6b961ffcffd853fc2ae240d50b53b4bcfd7f9c6cfcebbf84714dec1d15d3904b8eb506a8a7f5d5d9313d195ed2655474e1e54d1ae6311721475de884b5118accd509d9f734ffb051f9eed141c22a16a68c96a9fb24d +e9ea13afcfa9ec4b657cade8c373d20c2c13b4e76ac74d00aab43922bce4fd990127ef8e6d65fd099cc55cfc42bd19b9ade8fa71eab087b3231da53ea39ff01b2e8114a74f108757abdc22cb06fcb60a159dfd15ff52d1234f8bde2f8849a78ae24a1091793c69b1ae9807e065c70692cf7278ce739787daef86864762f7bc1d +c886f6a02b642d27c8606a4d933e9ffc1af53449324d5da647a427dfe244ae2b2b2cfadaab181298b07dc90c1fe6a900f1aa078edd86fcfb3b9ca8a6ac2d18248c25afaa1ca9bb3d391b15d277a2733f88df7eaad52d50449f085a1f0ea81948a38a7798193a52619428ad1cec3c7426d922f0dd8dd38234a82346d59e7a4c75 +f648344d2a961d9585105006b9db58df1a1c0bc7bcf2a7d46a5147a92a333ce3de0e8d2faf815738dedff3d6773ffa6134635601f9f4e491a184577b224f377c8580eeb35cbf8ecd1f069d825af966fd84cff6f2332e0fcf3c3f605d3b6fb6cae295c2f55c10d46328f8f8bd29d6489bce95ae1455771b0cc1c807bd74c1bd15 +c79a37d4b999e7348b78d0fa08b7a7dd53ac0b4beaf0c3ac6b001cbef235b42385075e0c81afb247df1d0b733894caa9dab15083c2fecf9d93706d1e060c143a5e1d2c865ea9f55acf25998ac3965dfa5b1ac476a9a6d559866c7e6b31cf78289a276bfae51a0479f899d25b9e08631f17719a5505f418a381def0a772b12c7d +d88b9c146f4e0199fb750c6666498bc1166178fc39169ebb5326f517181830778015833a4d98de263316f84c6af44fe81c6d12369e9f77ffc56f9d5e64f612bdb96e7440f84bc7f473a5f31684a1b32ab72595b58dbedac75f6d1808977ce2428f8a2ea87e3a50aa20748b0fdb93af66ee8489b00de02a62eeb1283154c8040d +e615ba364ffafd0b9d68d3a9de8600f09d08ef321235bbbf4a96aa37f64062b87e8959dbc99a9cc3a43b2829daa490a156e5a347f380b2cc88c2fb01847fb15982903f64c5eb91bc2c632500cc55534469709234c3f75a6a483b122c98f2ef0b43835d68b96284d81161f9dd7ea5f3317da0b8d6d945e8ca1ee2c36fc53b9125 +dd10b88f1b2bffabd48f5372f2087f5042668ca3035dd58325855d8030d3c2889de3d01c4c29309b4af3ee4478de119663358c4cd43bfde822ca3d43d85eb6a9df6702eba7b55321745f8364cf6a6f025bc7cd0efd724f8bc57858035318cba6f0752a04e784ea9b87bbe4c7b6989e66dc214b2038eda8c74866131cccb81955 +d1b0fab56306f78133d466ee8ef5f25e03743ce6f9f0cb3be4daba3b6d38d9d92f3eb55cc4e5f0efbe568da82df2d4a7e2488709ea08e71ad8d6c223d19292d283f77b7d226717363d4472e57c067790c8fb7beaeba402585796df83e7918f88a58698fbc428a2cbac30f09e647b2a36332d006bf1f3cad6e3a3b31bd88a270d +ffea786a2f25d52829f186a0f33580fbc345298a69401f144fddb4706962a2d17281ffd9ece1088b4a0ef43f186a6061785abbf876e79add9354da10ece0764c68a15c60afc504996564a7bff20623197d2e7e92e94d2b1716e15b5fba48f42afa481535f5c861001f7ef481434c7a3f144e5839313c3730f5c8dfd8413abc25 +c423dddf756797d8d1f4c3d65a1870332e1e0f38393ca7d1a1682b68570473ecb473195464fc3d5088e64e04196e83ad79ac888ff490f7ea240d2e56295adf29d069856fd7635069c9964941d2bdc3a171a9e56a67f1722ff9b3dd6493de3aa153db2a8abb97d67f7b8952c4224aa4b2be97616d53b3eee164128caf710ba65d +c9674f220b3a2c58df851f2603bfe4c908e2945bdf89df61d2ba9094e4cce5591c943cf2ab2a40ee664723961a0f353c0bdc5b85088e45d12a7d28336557eaa8224b843882b0dbcc3bdbd766fc2becf05348f2bfc85c4cd472a7bc264bd91ddbeafc8df06e53823d06ef34565439b13d76678ce32684f99ebf039b1aa422af1d +e78177a16356284b786a93ce78a6765e11053d69177d7aacdc500a5eef65cd61a77fc39b07e164684361a378bcd99862ed041a28e9f8ac7da2331907bac701652ac3d1a618d76f6d68781c82df15bb465205c6f4dfb1d7e42f1c2997ea578a37e229a6b8b8318a758ff684d58257ad18e955bcd99872bb3df226674858b68435 +c42eac8e41f303ad0083c1a790c6c79d0aaffc394020cb344fe105a9cc4190170b0fb5fbd81c77edabc17fc7c261a5561f34edcda4688369ec781ef4d486c99af5debded2faf3fa8e96d33999da49bab75e89fadd3cbb8ffbc2fb0f8481370080c35dd6aa0e88aac0d8955c5395b6655cb8bd34417f0686b03dfb332b7b39f95 +fcac3e16eef5fc593642ce3bc0f7dcfa5d3203afdf4d914725d22449eff9c53a56ec94c030cb793c870b78787baec6f8ba18c6e0ab83cc082db8f1d80a5ef4f19f4f775d0581b2c50928aa9bb552c7fbcfc26ca785df3580e53c649690cbbef9d3e9b168ba18ed4ae9144813c09c9212df7a25971e0cf39d16d0ba3c31bea345 +fe925c06ec7371c2e002573af3bc6bdd0db363dfce1fd3e1ab46a53e9a85aca5c41a00a91294bc02b8f672bb001c81f29a04d560dcc1c83a7e379be2e6863a97bf82dbaf58794885d0a5a08344d313a5966a667a7c9149147b90e02e043e8474d8097940d6208da26f7ba17dbb94ed8ea730610a27a6d19db0e852f25d3788d5 +c1552629fa38301034c6cf7b05e27566ba9f370474bd4efeff1448d83d288144dc5a088de234b311c8f9182c4496ebcdc279e299a654a4fbd5ce9d212f4af82a6f4cef9bc22b4e2f05beb2820843d69b1e245da30f501aea1f9c2f498240ca5895939c97bd50593b019e1b05a0d1d5b3acdce7848aa111626851878d2dd3d0cd +e74a8165276844b8154a45f81e0dc7dcfa55d19fe3af14a87ef0c39ae2f952ceb9370a1ccafb78ecaf7278b4470dfa26cea6e901ec0159f3a3d68ab0022c8c2c118511dc681e00fbe2a03cf9431e6ceec99f76799053f1cafac04e94895ecbb46e760d058ec20bd9823ef56d4f47bc67c4fa449c3b795a37f0997e34932c359d +c8326eb60037bf4345b9da19d4a4b0aaa618fce1b1b636d983ec5ca5e7ef9a48295d6fd5d2805d38e311b974f69a5dcca40db2db8dfff01c563bdcf623ce76a3c304dcb8e21df3a5ebd78abdb4883482ef0e99b3ddf998812075be2bbb73e53bc69f93c0c925ef9574c42e660d94375ab0451557934b5d93e9ab5c0160a57b9d +e325d4d545d0c461d1e6cb5c5c88b2fe5a6498a5f4f33c3ebc4251f700840fc0ce8e6b2c5aa4ebfe0d47aa64704656425bdba03ab530ad347e3868af9d32e743c085e48550e64a8846eb6b3e6b57e23c641105153807a22c7b929461c47b98b9980965529fc562c0c40389545d9c93520839165dc9f5df9519812ad49f344c7d +ef6fa5cdd389e45b4a5d2773bcf6c52799445e1a00b80e0af49bf59ff9dcc0b719973775ea3309190f9ed31d59c1e04cc2fc46b5807d51bc4b72e86845de6b39c85c809db14229905bc93c986047f127e7d2ae02184b34b7af72ff1bad7fd0ae398e631e095dfc480592517459b551829f814e989caf154ca40e57084b0b19a5 +f554cd79a676ea46712f4e3d592ad62c12f6f5699ecf30f65046de91c2e47eacdfba5ea7d691802a9a57111070daeefafd93a3efbf7a483f58fbe8bb3e38a20de9c438f227710407c9ea34d6cf304ba18e94f8cd18112bfca6bf7256c02c53446a67a66b5d8ef28f02a52d90304db070aaed34d746906e75aed077ed397f5ea5 +cb4b1fd5fd6ea096c48409dcffe3103a32309bb2ca283475ddfebdfc6d3f21d1a0aaf28c63af15fb94217210d76078e43495bfbdc8ac4a0ca483a4a84fce13c3f24ed69d1225225fa3a39df9327dac09030bdb3191354307db701183acabe5b9f4049a98084711d65b76657bf957ab0e1d51d18debeb386a4a4692ee4b10894d +d729e6b7386227bb2cbda5260a2d6d3c26127d5afb20f93086e44d3a2b180fec7ec80d0e6941b1e32d4a65441504557af88198fed0f8713c7ca89f095e1f3ec17174c6e138367f247ddce75ccb9cff48fb13a62f5feea30dc6dab7d12ed70a5779e4b890200c28171b83ac8a348f4e15027e34a89dddd727846edc880fe8a6bd +d74900ff226b5a70acc71f89a6b928ef282d39e17ca9cd334c4262c44837259ac948b9487ce4be7336b9829b9f48382baec51e5456be045920ed2d32b28b744716617e83003843611e42ae9183cfe86c0b51e514200504dde737f86e70c006b996d2d10ca9c33f733dc29497633364674de14e2256fe2b4658755a48226ee91d +cfeb9020e28f7ac0276b098df6fdd9322178b14087b45b09f0894fe673d6f13b51937a0fb6f4f68ad54bbee4831218ba5923d60ca33ea79c5b4304e773fb91d2026b00a773aca6b9d05de79140afdc7f2f4a160665f9e65398967f2cedc78edbcf8f1c1b9b0a6eb2d9eb43b52e58cb7ab716ec235e78106843cdc5e21b37679d +e280bbcced0e16442aecdb68961f53ef97afdfc030a2606d54e2b5610d3ad6f44165cc2c9500a49b539565f24bbecec5b6bbfc8185c815b00ce24bbe6f61032d30d611ed5b78f8adc5c2c45f82deea75aab6c64514e0cb3602a2b612beff2c102c888df5764e1a7df70794472f86a114ad17107a259535f07d27cf18c9434a75 +f719d169442d84d3858146d5690a18c28e1783be909379827ef36bbbeb72cbd71cd66568f2a015c3b4bdfdd2940fd751af4fc370dc2df02d2e836c9e6c27ddf866b646bc05046aa4a3ec49281d9f03a56897b625d9d884ac02e87bab4cb8e9d875f41895a1dbbd77d77c18e67f55961888aa672edff49c0ba6750d0c97c659c5 +c5188adf4708b4c4e8b5da09939f8241abfbb4735ce569ea05fbb948d34ed13408a68032adc0cc381858a100c202975c05f64d5f240364fc699b4b0d7170dd6b6722f46ca9f464da1eb915176cb62ae58ede202dec980925d9a4c72d55d4a1a617c6b3cef3500ea5695276f3c64610703544fbf32b9cff064b12f959473b5d95 +f848376b6da6b8c4a5a6cd5c091f87aa6861d5f074c4656f1d436fe455e222c778f2f9de22e40cf831e9f2978177ee80e29291ffb6728c7c2317b55e91d9a5ba92e2097a9a18fa975b51feb8c9e720faf8b1595f0c6ec30432660744178ced85ee4caab23727e83f094ece52a12ff9bb473e7c1767ca9d4e5bef3b2774be0895 +de46df8ca7a7d1bdeffd0154a914705de0689253f377ba9d71d0fbb4ef39f37fa76585e83f3f0efe5dbd96d916bc6ce65b5f9e9854740e60e06cbff56b74b00d5c1381c45002372995dd149a0c6e17904b356a6b4991dad55880e146b2fe6d2f5a7923b3ff93d1efd5d832f0bfb3f99d5aaccd7a33159ac6cccfe8d2646033f5 +c05cfce36415e5e276a06631d8bd157702acfed8578334c7a8834404723300d6eff52261007b514cd9d8af7eb18458149a71727b5e6834f019910b098a56be457fef0a1242dfea95d6d9185067e7b1bfce3d398373225669aaf922a8c7f556549f050a0e32ec7d4dd8b60aa9f5ac5136e07e87d33d10b41c51344e7c310ff445 +de846c4c59e11179be5e1532e39df29bf5418a0482318bd8a2e86b85f9f550578ca39d42f4fecb8d449346946d0ef2ea58976a392d5597fa171f2cb0b89339ca30a711c02625232f71caae3e51fc42354307508263028f3008b579180acdb4be0f8871ae4beaacb30776a1889117239af7a523d35477a0a97f1ad09462e9349d +df12c209beed0ea2f3642937597f2552dad5e523e29d3ceefa2a6497562ae2d165fa88a412de4a3b9c60c066c57647dba4b62daaecc6472e8a5494a40cd795008c917af115502cf37bcc20869dcc291de45bb0bf3afd9868949e3419c7f573e88bc04ec813c0efb58b7e8a5993de4ce67a8b75efe0ffb0b019e52e3cae42a27d +cc50771c1648fee05dd7f2b11aeca10f9c98fa92fa549e7d58c9addbde156a9da1479fb99802524f074c666e1c5073896613a3887fb9596e963564f423179d93b8f01e4ea28aa4a0cbfb816cea5093a9514ba234d882587a11005bc9b9f78afefa418ff26c7d9955eb50610f9b334f223a7e3279f879e645a700430897ad4225 +eec6db480d9fa186cbdb38ce166dcb9fbde5378b3b7c02e8cb9b0408cce03f5345a115c1578ba63ada5a63f3a67ccdeff0091f145c20550b6d0681d7b15d85a9127e3609ddacd1fb051958b0f0ec9684cc64d2a489144c6baabf7d54b87208d2a5c90b3103b390be118a3857a6cd8f81bd58ec78874d5376efb3f64639e1af45 +f9f7bf19e35163b79760af714be892589c1ff3a4e640d2a95614e5bc1fb99f9e02780df153bf12b2612c3bac9b8207c69c576011db501a4e667a289f8bf1956f600453ce00b57da6b79d143d69c31bf44ee4bef65a6b0a5c4f054e92c82b2aae00c569be359f40bc67b221edec8a21cf1d5fa2a734dd486c9da49af2e6cc2b85 +c55325a6adf5dbecac33ab7115fbc5502f1af5fa3c39e2785f736003f3f02cd12c172700b8063eb97c7467294ec30a80b523398090f6a6536156e53e9c2f0504fb2a75b2aaa3d989baf6aaf7a9a3f9a80e3ac23b3a05204cec1b1d31f0879e9d3d0a4229fd2af6809d142bc8a9d697b7e04a8f03881c01c46caad405dbcd7625 +e55ec0d6b7f1a14eeb398b7504ed6feda7f2e8f79ef3d1ccc0e193311ff39b73cd7afa873e4e03ee95ad3a8d1f1dc8e1999dd2263bed74c6c46cd061eac4544fc8dd67483a9b15d998128346228151d049077f83a7d950a058c5a6b89953694fa226dd1822c8196ddc75911604330cdc09e5c259ca8b081c14a448e9d1fc47ad +e05c896d23616319b2041fdeea81b8da5077aa68a6a586e0fbc189aecf29c432e46e1e37e515db6e2674837a469bb3f44f7572ffb8abb225291430ca44c7eef9e79a7269209362a7f60c014fa5568a98949809cc93fde3ec99ed034dc7134e0d2e8f28f7ef38ded6a1fae3bcb147de54228dd91f7d88844f31c59882bb435595 +ea2513a18692955aa60455f8fe637f48cfcb64f497063a5cad9f7419a1396c992a9a8e9a55615488b374242caf05a80edf2d871e6c65206a1a27566728af9e4f7581eb1741c0e88622d14a9d3c840ed68cb2146d22995532b820771773b2c416a853946fff2a41136de6205c149e3789e394d75ebaaa9846af47867bff3ac0c5 +dd99490b49bdd7f025fe2ccf451f8b25718d1992339c0a1c62169eb3da2d137db4c8709ea60eca1e590da4d20f737ad582c937ccf364f10c593e9c336859fa403de6f7a5b8de16a41b5d49eafd98a94a8a6b3218ac696e236e46307e20a1929c5bd114516e6b6a6f4466b217dbe03de266e7342bdc3384a34f13116c57d1dc7d +d6366000987a0bc50a837241f9c5bccad07a851d10fd8ae8d9bfec1bdf8acf86ab98b885d16e89827d0ed7522a3e2aaf5fdc2d46e2fe3bde43bd3b8fcfe77de4f4ecf80d6489f12807d57a4ec43deb0307b94ac5e610a29c75810a56b207c2ac8747c5f40b6b112f98c0ad59b6c382fd610818630967bf9d8061cce453086f2d +dbaf7fa3ce31316be0fbfbd2f5439fba81d440719459e61d5cd52d6708e3bdb1c51322f33133b2c6f8addaa143ebf380b46d958723fa9cbd0a470fa1ec34bd5d2417380d5700827259bf7d058306b766d600fcb63eca7519ddea51e7e170521dc2ac5b5df245e5dfc19a3c0b7599cc80d920ee5ff31ffaf3585c7c5791c741c5 +f81d4e3e2ee193d38eb2025316be6b7f65bb6625666079abc6e0c7b7f3258ea7c95c53769112d9f83846dddbfaca860519fb3f17e1d1eda1c88e6df313682f2d8bd6018692865ce0e413c72b1882e315494992173477e759ed05766fdd9769c69f852af3dd385aa9d39cea626b372157ed825bd88df7258a1aaa78a0ffef69f5 +ee3fcccabd80601f017d6b760e12f14cfdc876c8e0190550d32c1f1eb6049f5edd356396314b8914a84f6178f62751e8c31fa5bea13d4f76f79c446bdd45a66a71b310a4ec1c85677288ba0ce73861a4bd40cb96cc896dd839227b2b53a358baa8cf0e70995cd7f66819c00a7f7811502d81f8aaa7a90d0851313f08b63e07a5 +e7bc5d453f823ac26bb071e138eadad94528a689566eab8884aeca68a4128603b771b3fbcdefa6645f3e16163a4b2347b7a63d36aff89e97f5b40894d7cbda43f0dec94040067e88d1c2627245462fdfaa061ced0ac4e9743140c1680b07c107e1493a2800f8abc75e403c4ac5289b59559a9fb25e0051ff614fe3c6314f911d +c6757706c39d581ffb923998c8d11656c261d23f529f1ea3dd5b91482fbc1b5d9d0fafe22b0ae8f7caaac98dfbd4fbad7f31ee044a8d467b590c3166abf56a5e3aea37ca2daf4c1a6815f4a503a7b1b55a3fbcf14f84ffb867cf9f3f546b02009de6d3ccfd76b691e5293240c342550ee13a4141a693071b629fd3770a01371d +cce03e4ce3bdb4c846c1044eacd7a400cea051d22c7eaaa0cb1494e2c35a282b87e0157bb72708a21b88297be011ebe06b2a93c28d1edf61821cb7ad6edd14ea4693821c1fedee39da6bc86cf65c31b949984db16ce23fa23b0d4f9c7d73bef86969173b7ff988854cdcf7a23c9b2e3ae8bab8860d85a5ff004349f3b74b4dcd +f3ce37a66647d5ebd8781e14c54b25e738ab57df1393db7bf1e71b0e5c2cff7e1c9aee1228c2047979a6073326c56e969577f196e58264cc3e95addddc871beb8c7facf010236594f33644576934e024a3194f7c168268b64f7082ee9cb96cdf6bd834899523841ffa8694eaaca9c2dc48a53e02199259c77294e2063664fcdd +dd5df28cf7821595a31fd36906f6802b3b402bbb1ad9870cf92b64acd08c8a2045a4ab03ef7cd607a15d27925b3974ea3a1f453c5d59c744343e7686a62737af11557bdc6862eff77c9341c988696938874064dc0b0e1212e6e460651c05edb595d849a974cdb58c498be6e75a8ee339004f121c4ed81b50e88bb8d7cc87620d +d56377fdba499c3acb75cc81353de33f5f626e85d74b9f72fdc0a265b19a7f4b34295a3aa78073097ad62d38575693cd8bc5c6306286891d201e5958c686ae7776a2b3d7d9352fa536f415a71565ca2a1637ce6c1c6dcd3219852a87e865d5d4e018269a20a10b4391cfe8216d2d8712174e1c6bd2d3a369dd005c913fb1d9ed +c24d97dece5958111a0a0d8538d1c15e282f21681a47f5c66035267a89e57d21287000214b069c5acb3ddf99e9ad4d18c02b675ec1b634394901796507a740c818b8859e6e74bdbcd433a236c7ab511131eba2d27414502507cbba401311e9153a615db6fa904d54d415d99970eaf7aaf3a265efdd2021b560eea01b40c1a0cd +c65535ed408e69a5928bb27267ae53024e6ea2a32867212a914b9447d07fa6b725e4337bee9b7b7ff99e065248848111b08d4bd028533cc9420e54801ae0b8536ff3659f34eb9062f1469d6dbd22596ebf993f89e35cd4366fb3de63886192030c477ccddaa8cfd1944ed3aa2288d61d7f931e42cfe939cde98d42bb92c48865 +ca8d20c285a7f844aaac144079b0e9ee4c92e160aff97d298959514223f85d94658219eb8a0740c7c1b59395ff6e57c0b9688e8a843d9f75974f5fab5dcda5a0ebf44dc271c22f0138f1268de1fabd7088f34f181c20c58479fd5f73b36c36b9c77d1a02c3c999298470dbd1a4edc324e3478220ecc4e11d3cc350fbf20ac525 +cb1f812e799aefea75f7ff591654d77fd43706de79eacd8f4972e4a10d433c74f66fb644c7a53b09ab8611daaec1b8b158995d1162f5cd66468d3627638694f0144500412822685b72e9cc4d0f0310cbdf9422943be40f675255cd36a624dab1635e6257ea937ec0a9c2218b3458b9f807bf604639bd3f2c7ea9df24bf7f9755 +c9041cd6b43bf14ef4fe6cd495f876bf7a3da054e826570bedca2593b715c5e6f02f5ad5703196ab717ddfdd926dc5ae1f4eb49f054b8078da6fcb41c8d828833418c883451e944bc1f62a02e87423aeb84f52a6806d6fcabf42ec19ba3e023c216d57215c3c7d475065320677210d19f1806d08fd6a3502f8e157c8ffc7ec8d +c5961a4910ba30ef276b27337bc4adcf01c74a83189305f7be816887901c6eec46d204e813e3d4fe85e812639403102dc00a8e2d54c858e54d69eb45076ec46b826c9f6d9eb862a5dcf168fc71c84b1ae6a58677fb16eb1b11f37e0844a84b8b0bd06c33ed5b1d61b0855cf220429803872218f736089e7f0627927c01891b5d +dcbeda335b757bf79323762fbb7b849545bb717052586afa644c0788e300c07b3ef64437ee07a0b181c83cbecdb49b91c4020215fd88302a232c8ac46a0fc1c1a2fc8b45b66f8e8ee21135b1f186f1faab41ece733401d92ec4cca066d6a6490d721152b5547bac7cc4f1027ee7c6edfdcd8ed22d04586fd159cff4ce970b245 +cf4169d4412da9494e211cbf5d25ebee47e7e48409e3b9d76b4bc6da0f91836fbd40b4ede0c50d05a63d6184c9495dbb608d3e57117d086d30e35b121acedc64dc75c5f7c760cf9d1a455f171a2fa9864f404f7188604a92301b5fad6d5544de323a74888906b6177dba5a643ac784ee4eefe15d6833aeeee55cc9a0f6d699b5 +ee2113d74d47670f7313c08e615fecd2777ccbf355bc125ab0bc48151947af19b99f2cda5a815d5c44046e74199595989d1ff84205a8a2c976f9e09ff0361a62c27b3575d1a53f3d3c9c2681fd9528a71e22bb630f9ff2b9ca1a77c54ccfd699595d97becb8764cad0383aaaf908368acc311b43980cf902885d0cb13eaff335 +dff036be4f36093f2e20781547235c6b88f82401795e0cb513d74e9618236af81d9daccda3da9c4a5272d2e10e49a776c7baf5f2aa40fa61e1003df52d603a6abc2484eb807588826d5027f5529fe01b6651a71dfb1ed23990c7ad12a6902c71282e9083fd6d33a36919e51b00d10a47582a9dac65b9d192269000c3bbdd6cfd +dc0d7122b50d0b27327606cef7fb6380d0e7f7297f5076c4b4de8cd76ef629745d2bf9bee4e2b1fd8f37a1cb11758e98d2f2e54583de2a3c0c339a8bc8d013d48d5c76297e12e18d03aaa6f242defd98b145f5bc37224c0f94f895132b6791392e7d598e0b39c2f9badf8631f19325fcac3b8f8ddc655914ec249cfd9e0d0e15 +ebb816b31744578bbb9b10c8c0217de1c20b0ef86b5bc6da94f5708d0140618a56699792225b3adf6e9a02b68422ac0b9f8bc5f676944b4bc3382059dcb33b5343e2bcceb340ef9faed9044814c41de3cf862e07a2bf936a6e8176bc8217e24169f9fbaf8c09b321e349f4d2364d9e37096ef8f62e0c0ed640adbff7bf7b0135 +c68591f3244def5bd01eb6f7a257842fff85f59e2243377e0d265f1f1b90e478b563d5085d1982d96ab881793e195a168c468b50c2c16388107761bad1278db7529d207c5a41ce77bf1cd4a5f6b16cc97b5186f821b15311a07ac6ef0df2994372f2f52c3803ccdad0da78ea6e7527502970e09dd1ae7ef8e99b872beb9f8045 +f6b4a1c781d759e5f4a13973e003264c365893c9f181e5410af0820d57ad417039913bcabe466e43e7e4d66d79fc564a627d3bcde846d5ac6658ae09444050515953337a9398bb1ceefea37c72c67f09cc0ce3f1a4091f52834003c3f180ac340819a4fa0e9fbe074f9d0187392ecbcd024f0a90649759ac83fdf7e77593934d +f6e7675f852d318d6b402d410762978cca8588326d5bbf84ac86160e03c426d90ce167b041d255993eadeccf331e7d0cb57fb25878da293cf2b9fead61e9e080f2488d98e96179693f4492cfb4b6df7b24446687e1b880356779fe408b09c07ae59fe64cd4bdb209dd93e37d6064f6ebd7f0b5d3316e77c916e515d608096fbd +d9cc77a38c2e2c6ada42e24580e3a801b85020808778cf7956f90cda127aef2416e931aef46688ec937d9d57401820292f1b7ef0b956a9ffedfb4fc5c04f9e582fb527d7e09d68ae5cfe1d99b47b603302a89fb5b933e18e4f0a1fdbe591974537f59ff2ac171bedc72ae00100bdc4891441c2767fd70cc9917234cb6f7910cd +f29f0718f61441e6a7fa1968060a4624c4e038995140029615e447a2ceaadf4faf0b650a448fdeaf4d2bd8a3467fbfbf1d413dac1706b7d4d345daad253a8e47bda4215bb71898c7b4859dfdf9011f4f14b76be402efcc22a3f65895847aa866ecd65492a53e7b024513878f197e23d16b6753655c7f2f67d1d5a4df6112e87d +c38400b70f59f672e31bafa81bc5da25f9eb83c2892534d863325df19d0cb4d161db7d52e5d96ca6d4884cbdd47f8f95a7f59f087933df4c687cff3c1c09c527e559a50674edad1f798b233b3a9e6bd0f1d2e14933ca7d27910e4cec9022fe6713b6d774f624968cb2f8a146c5aaf48df2a44171e90c98aaffc5b45cdd0f5295 +dbfdf8391f3f46855b0effd42884211a069a720eea40b4045e05b3f7b89350b3baa9349542f5dd48e6c6675799ad85b7ba29c0d7163f9ecc8a5f945e9ab62cbb3c05f018e07bd736b3d245c168ed0e7c565fa8df0dbf75649acc4a078f625cbe24d7d102bfeb66971d97774e5a2a686b7484260e762c2cbe8218412b255cbc4d +ff1044955a4a46fe88719b98b4fbef3610210a11a00f28779409e52a898a2a62d383379e3892ecbd5594fbc4c54d363f2030ac48f0cf8031904c598d494776c399cfeb0be3b0b376519e82981cd88d0dbb656ea1896aafc998a00c33a7e8e619fd64838b70bbde315812ed2084ac77af17a5c5737cbdd211a0559f178cbc796d +dffea7b7cc8dd939086609c14c85234851f74699f5df1de82d8f9052f8d29c4e2f5af05fcf1e0111d0da7d216ce76366684271c14e56a56efa08592f7c0aac245aa1d4a0acf19f921884f50cc320fcc76f1cd094692634e3bd5142cb588e62687705b236927f77e4097724c5936523fdf6633a934efa57ecc0bbb109e4301575 +fc7089d2a8cfc5cce0d344cd5ef8b83274f0e49f7a805299bf8adab3e89a074d9daa62f7ae3b8127b628a11044a511061ab08e7751e9603750dc5a5f46a46735b5901c118a2a839d1232d2cbb429c0a7588847daf657f367560e30412e984028bba9616754eb2f2dbfaa3ee4550ee81898d933eb5c4339c1f126bf9286e7721d +fad5e667ecc443e6caacb6ca76599adaeae84cbcb83d0713eb84f13dc7fdf4f0d208ffe47ea4a1d67375df6a08cf13067fb8bce071c39a5a75e94363964125cdc689e499394d3c7d6f0ca6238668024221f82817d132e981c72075db853262cefa4bbebef031efa99dc6ef5384cf942c6ecf0746825c9ceec4cc699f2d4181e5 +fd1b4d7018b7346ea508723e0a7d3960a8828e7789230e1cc749340be4c93b4f424f598b1acf0d16dc515cd6ffb9739c5d52d6e8e9e570c355a0dafef9dd7d61071b0b4ad1f9189df4284308d74c34eb8dcc2d9b9d8fe66fa96b0b221881dde85a8583145463d45d7e1abf1278da2ea8050db5173deb108d12c70528d9c4745d +d58a4da763089093120a6a32735137d1ada1a5ee779329ee0c3c04036ad92f36ac3d4ece8e47e6f3ed86ffd47e96e36c245141e64bc20a825657fe0eb15ca4dd9456e59f6769fb3545f5cfdab573f9ac9ff9ad1dc55f012589cc6c58025df387e050d52ad8946f9618fbce898b651c8fc155827229a779a6876e92dfb484ca65 +c9086f19fcd869343b4a3c21ddfc79caf54d812e21049ee968aecbdbf22ca5f4e211ac94595b28f9f8a3f7f21d5a7c95bbb92a5baa0906ab3ed98694a9a3e40dba216006180c472e993fb1674cbb02c5dff2e77d2f9d03323bb0addddb2ed040c1c55599377d7e98cb0386cb2f95b8f57182ec2b982cac39f01243bc0ad89565 +f461eb483e0ab79a1a6d670c7613f6b04b6f3c2ab1bf12bf20d108f8c9234e1fdbd4502bf829be59b4ae68bb90fbdd6d5b19c5b84a3a512190adb31df2a4e29b5fe4991ebb00532365ffcbf462be7af9267e06c252ff251093bb6a442e1c88a0936aeedd25d99513be8af23447eecb1dcb06ff24ad868e44f53c790feaf4ffe5 +cf62f209d35a652c0f29f916da23634d32148391c06f3e9691d24758b87fa9dded4cd75928a2c4f3a097a0debfcb6928405e50f4508e16482c30f4a6401e1d32517991d3e13acba0397fd02483705d3500f81cfbfac3c4fe75d755999a9a6587b1ffd34f71a8ee827372616aaba8d7e44532c639c104993815efdf23b86387ed +c355f05e36c6438f1fd6ea0613892c3b71931a3af6738797cfc8fba634d62c69b1a803f4e779e3581b1b834fd9ab5f6424cd08f5bf242f71435944a2c31fa93bfc8ff5baaf40f5e378962fa3c10cdd5a1bc8dc0a0ee910d6d7182f5bef59bdc4ebea9f46108f0c78192b6cb94d8e259be4667c7ff4b8b48f51fdd34600fbd7ad +e9704819bdae06326676d9c36cee870a503273e9e450594b52a16ad584504f09fd19177569de2e3c2889d461257b6e69d323ddc6534a672a999118967bf283b012fdf87d9301c37cbffbf49eee882aac5dd871bc137396e9a184f1f11105cfb6b88fb8665a861f0894ea4270fd93130a382b938c9218143345e3f7fc6d24bd3d +f935d2e497d834233aac83a32151b1f19adeddcfbe5145a882e0faa6a53324dd21c79e5677eadcfe93628a58f70bb18d004fae06197b01293b3d09fdb4b7aa0ffbf7059c62185fc4a30cb957fdc13062583c3ed2f4e4588a7d63056e7c2adf47f6467697c6ce15cb542e92edbe132c1d40fd56aa4de0b107e3ff1582e5c0e475 +d51a8b2cf9408add3bc549a140310d4c8df4d2bc68ec35306ea146b90c2d19197afbf54891a240bdd1057f3d8238578ea1eb2e7c0b54676e4b53549fa074bdbcbe794d9084810ab6e0b4827c739e845912f54839c37031482e5953f04ca7b5c39d28c7d97646508675386085a3b59b011ee2b8aea1f46c052753376fe5e027dd +f7048051b96422dd7f07e4fe6f1b70b95aa981f6136cf00e05f8bd1c90d54e425bfecfdf1c5522ac89b10419f898bc1c0616ee8bc703c0dbe4835ed1f56ae159685535960e386e662ea504c7b9bdb1ae7a0cd46e79eaf6e72e5aa60cb58e1c91bec7258677438198287296625f65c8e77db2c44da948d8a2f523b1e65e1f08e5 +ef81839f7627a4798900fdaef895cd619689724ce0b8b3af8b12711a1b7c4b89cba3957f176d4a74668a7e7eaee137f9fb144c96355e1cc7170c107f513a2a09d3b053c8b1b6fc9134c084e5540b2f698190d43b9a1bbc0d0cb77d3c526a95eb5115281fb66c3e3c569460d701393a91e41a1c27be304c6d4d352864736f791d +d06d19436af8376073fbf7527fc3ef4b54e6f6d7323cb3fb4de5547ca75c7aa5a975665c9eb6408447eeed168c75a94c24bb7ea1047517a2991d204a10fe798d24e57d0d907080935492fe3571aa8a97533d0f836ac2037023d22ff0674954a43f144d5ea0755cd6b5a64a5f9da16966c1a40f757081a59b51ae50371ccbc14d +c91f81ae46dbaa65df1a6a5e5ad6e663575559384d94123b2b25c73b67e9d80a56c1e7e503906b87d04534b8d1e0ee788e45458558b4aebdfcd4054871c00ed3f1913e25c154619b787d62fa362c92242189f3b12ffb89ec239c20fb9852900cb73c5b6aa681165f6692321078aceb4da6d8c6753038a624a65048a60903e72d +e2b88ee5f593780e9536660e1e277047f5f00f2507c30923f76355443a88bd194546598aeba7d8a594c461973c066c9f1b72514ba669678a85b5706637ae059459d99a4d9e1bf1a95988226df3fbd51b0c08bf65ea092870f902d734f2326c1c82b28e32154b7044be05a11d4ff9505e8b22e991bdcd295fed0d7d520ef59aa5 +c51bae95613c3cb9ced85c822f9f63284b4f69cd5806e5f17e0691fa5531d3a83361e042b796f53667517851bf533915ade6100c5e35e571fe62186af6a2f6b111f7b006ba1d2dfbe15177f0df69bf48e59bb15c38c3e5f2adc861b99c3ec18e86cc2ef60765a1bea21c66a4a93f5ca5c824a33e3df049e2294a851e607d98f5 +d44dd9aa118e09e71722661f274ecbe1dea8195148b3be5788e0581a67b10c54fd565b1206442dd2e6ab50d0acd466d4190fa2b435c93e6b3599f38ab8ac197a97c1b6eb85ece86f6ffbbdca079f844b9e7aced5a1362415d7952785bc3d7f7759cc23fe28b1e7004ccc809956462a5291d03ea4db7ad19c160121bf81b6ab95 +ef78419ec73c3b5c017aad711d0cb29aede2c53c6849a804fcfdd1e4e8a32d26e5d024f3f621e0053c03fbe25ef5becb19acefe10688585b80d1504f0084ff606d37b8c630e73f15736dda94e82eac69d73b1fd69ac9c0af1343dee6b43f512d93654a9559331b93ab76767e5437d42e8866b68140b1c7e8a8de2ec92b96d175 +e0b9a8df9507d8acbc32c9782d83d3a67e5e9601c155794bfe29e065a4f3bfd5c923001a143473e1cbd5d9820b73bcc1014bb1a7381ea8402a01734683529b599b19505d9bb7eb270c9b64a57b98f97554ff8380f79653ab19bac8ba59bc057463c497239831384ce93c4da48c0ec1fe94403ee965f3856425a082d5bb9d158d +c88f3d02089a37c3ef9de421da396c22884f1934cccbf6a603eb689c1d6d503cb9af8f153dc2f2b4a57d4e3731fd0ac69bfed081027d0be77f299903ec52846ee784024559b5d13ef76dfa4b365d08a6fecb2fe5621dabd3963a3ff799f1e95466633169ad5c5ea044286baf5fb3a19a2a7147affcdda544c1061da877b978f5 +fe6cb780781593d1e777f946121b55f15059ab69f8c1ded35a09e6c29dc4ed637bf0dd3e11a7c056af7c7fed27e98bd80a5fa2ea7665a82b8381261c7e764f02fd5f605c7b8059161cb1a1e11f7632c90d417b460e32b3ace20a6948168ff52dd64f5c17a412b7fa72674baf8c1e24ea62a8319b3533c4dd04b840083179627d +e14376d91a06128b4239141f7d4b1a16847d475a02660e0304396ca40ad93b994c6d501db2cc180f466437841e76f798ceb5e7523ac41b4237064b2b31245df30dbcb6a076cca82de707e1b45c8892556f9fce262ef24169cdd7539c8ec04f497f5c4b28262a2622a650648ad9d4ae5e1945a443562a8de81518b22c42bb3285 +dd6496c4c984f5305b5d7e114dc2184f75ed627180145f331049bbb0f15e14ad2fa74ba7540206b61ce364bd191230ae6ff2d8c25d378d6ff9e8d08e4972a015b917b43ecd734d6defd9961cfe7ba50b916294c9e0cc686c7a496eb5f44c98c44ab974af71be0aa50f5f47cb902a6cc004289ee1e882f9a71bd63ca3d1160c6d +cbdab7643480f2ce10c7f4f3effa1491d7eaf45109b5e7e188cd5d8cc3d6f17b220d774844d4d130edd0ace88a8758d951187405c4dc67dad60c15eb7a2b7695393459db06832e96bb017623dc617c0b916484542633c5c68c0bb44ebe306d9ab1d7e33fd50e328cea3f256393a822dedc68c8af63388462b1e5b2ad9b72cfed +db561231fa1ec28360b09c579bb5cf6d4a8038139fff0994a34e325ede4ba9e41cc65eb04294490880891eb4a624cdc25777e8ae54cc65642e3e986d25f0d1d68450df0bbf52f2b0ee5da9769b6872b39abbe87dd4ebcf56f16c4f8c59f7b27d49e84676ee28980a68fbac057e0bbf69a5414859aac4eb110e18102e4d356d0d +d13ae235284d459e4228f01ab6c8b9ca0df6eb563c83471c32d272e8bc5708f1dbb853d4a225d019b0d63d244a79bdd9cf83152b5c568a5cb378ae601cd96864cd2fb65a1adf1e0fc8cef28c367f44705ab153b86018287fc73892221877eb2f544da6bd09ad2d9eb5285978a49fb3536b8ad70cb0b5dd96577b4d284ac5c1d5 +ccbbb29d9ee1e4f113219a526ca77d70a7b00fd13f9ae423d287c8fec3fc4d128a3f29110b87933f86849161f57c1b72c9309fdcd52390535eedc3b18e61acb6e904aeb4eda536a74f53fd02c9fa903f2f60532baa57c26755168b02db0c3cd311fea7b833ef308be69e8e4408c23eab0ccd3222231a17b74438a8a937746e15 +ffefc0562538685dbfb1e3184c5c855dd0a04bc340374386134f756fcd52deaf24010cd012ea90306be7c11128326b8e940a441ffe90c81c7096e11431482a8cc42576034034403b8a5691d28538833f17313e610558be716afed80807ee29d20b309106c06edd2e7cb06f905af6fcbcb5ffd41f36acf88170330394db4504bd +e3081868e6bfd443207bc3b51e53884770f6dfc227f5c4ad5aac48192c838b7d53b753365313afe4082f5763d93a9362f362434df93b06490bc0df7ed1a3e69811fd8e2f2daf8093ed7a3c55744a8e2243eff50570aeb50180d9cad962af13522d80e562dd549840102e4af3b654f62b29a8a035d59e04277e3b8204989feabd +dd467258d247ee309beefa0333ad4bfd4acffff39ddbc44e6cff2492c65f2a9cbbeb33b915141d7361e579ca387ef3e55105b39db94a006dc92f55d104b29b2d9cc52342e58ac426c5bea0c600845091de3ec35bff5ff3e181c8c13c763ed8ea37d5abf8b39f4583034cf575000216c48d56338650cb39a4e1806c7dcd5b3d15 +eaa24e93d42d27e4bb64120d554c2ee11a095f0f9ff153b835c94e9a2133812d5ef3a22be995af15f6b12b7288c048a6fba834f4698e77259b5a1cdaa740d550383c2c0be7fdaec742ba71148a08d4934bc8e719edc0fe65c32ebeaec3221f7566b9f8f4dc076a0eff4a35f675852a26e46914c5bc2f1012c8dce0f5f6b71cfd +d1ef6b9fe5c0ce5ea98ee2851163b31dcbe3626f2c61e0343c3518d407aa23e14c1d9fb268c844608dc939434e54a5c20f2604c1ddb5fe4f5e71956a4114021758963fab0f569d82a333be13da8f80abc81693de7fe55bec0c53bb6f2a92f9ec80ca756367aa767b39d5b28fa21a232e9f524a2b08586e3e2630c073710083f5 +d0898926fb912d9a6a570a895aedba60ef0f47192b21b7ec097f40cfa4eb92ddb3af30bb55a8dc66a74a68fcc908f19abcd9951f06bd9ea57d91221135c90bbd191b53c03296b8e5127978ab4a7538b3f1a975448474b2895e24eb45ad71bfb24de5b98cc1858a25d1d41a0ae575b05db820aebbfe82a1b7f362509731780fc5 +c77eec2c2dc7baa150df52db49a9f9d624da0cdfd2f20ae97d27e948321afc76aa9e3453dea6d7decc72c8d67da56f57de1874149066b20726ec74b2dbd109bec0fa05c735bcff108ba3e966f00f6a5bfe868c5b74a7983bb0d3310b80b6d745b7a288a73ee40951329aeb18f1b5054a4148a89c7cb34b0f5693ad7e7f29577d +fc417d0a6a65e7a90ca52a82a7ab97069e9845a248fde683451ae12c6fc24391063da1dfdd12adbf2f6de878a47ac0954c8bd6a13129ba1aecdc535a16feb591ede4a8dcd5b0aa9d40129dbaf29879af5debb7fac908565977889ea4857c27b6fcebd8f0cec0fe5ebf1b0f389205ad4976726aaab02045cecb7f3440231f98ad +d3339da1f1643b18af3aa714ab2fef6e2f229821d1e9100261f4f696b972af044ee21a2c0f3f3f0cb69cfbbe06409057a02e6129a3e6ddadefa12639ca150eb9ddb0a2e4a0b5697e4160de6d885061d71cc5ad6a19c7e002078e8ead1348a3017758cf3cad68d70865ade96f3334d708a34d5931ed105cefe0544e78a4388805 +e9065eb79ee92dc6837b8aa61f7e99541dc966151f34dc0b97506a5666090d58c815490a4170097a4b5158aff94a69eea153561c7e332b387c42f3563ae81591e908b0e2f4449c943e7586bb4aee61c31d9640f440d08fc1bca84f8575e097817984e803705a513ac3c852b702d7675d5782e365eb76995d1ce292194039370d +cb211ab2c89539b83d3b4f8ef0edc2e2a061dff45c19b8e25c9905049323f59e42108359467529f94df66006d2b9409f1add75c7be360e29805570161d3660c96038c49bf576dd84f6f9b12aae23cc8bd268ee48c29b5fd3c13c939308214c759c7afeb89465b1bea95c292a0481cad727acf3020a44cd639740be98cac94515 +d5963124a849989d338e6ee7ffaa24830b21fb0b32e6313e3ea77abe09b62ea4bb29a31c7df9c38fdaa25f5ed00abeceeac3c79b0c33dbf19380a9a88cd6cfd09e5bcccdbdd644ba3e018893800a910f2e99beaa7efdf3358ce600d20be8020dc4168647e9a530ff61673bac47ed53c63b39e9964bc542b797de0a7517edc385 +dda885206c316fb8493450432e705e2f202dd4d5a35f86bf6332df0d50729aa3f86bb71ef78d7af36940a1add9f8679f0b8b67e828755191f76ff74e1b0530a9a2fca1120b32f28c3e73a9e51f9ac3be261c91919d936c66e2da8ef0b23db3c9a6b2c3e1d71fc634319e99e758513396352fbc9c978c3e8746e9dc6e943655fd +f0b6bd708eeb9a153389eb769fb6b140127f876b4116fc7f72dfe4e3137951dbe9190d6f76916dfb8dcbe8c8ed02848386d8d43298bfc1d6d6daf773156bac1cb2463ed1232902643bb090eb604c812de60c1515482bb3217660e11eaab25865f375d9fb7a5257c80bb5019056e233bace203cb691fa15831ae1302901a7e8fd +e6e9ede06461fff5f9fa150353b9604229649e454bc1adba9d30d58fae3f9bbd45b45c4a257e71163c9399a1888741cfed9ff01ab8167381e6f16e6e16cc2aad73f6c8c30d1d0be9eceaff662984aff469d85cbe852086beec64a5c7a2448720b384b79d046f4c41408402621d2a676aed29d24eaa99088d126e947f9e240475 +e99b898150f7a8a2d907745b3cf293f2484957a448f480fa2fa9d1069ac64ee0b34fc1816558093e14d8675e12dc416732f6102ec9cff519315806c2d75bf4870a2aeb20abc04717a8246b3ca56ace5db90fc60b820b72876d92a6a4ee263050d4a529ad29d87f626f5e9c4385ceca30076eab6340e31adb05490e3fd0228ac5 +c2a9ae48163c80e187b63785aee50b61cf8cafa1f41355a404b3bdcdb1468318ed913fb4294b51e7301d92f1970dc01e4320fea142e29cc8c10d083a6b6523829a13a762fc6681ef0622ed11d81108fa814b989eb435dbee63e025f9196761850054ed2148c07387a5f8f20f6623f6531b4cbea6ad00e8398c3460edd9c3b48d +d5b9d5961ae8595d6f90402dd03cd9af9194ccdb9c30db8df9e4e3951108f739a6b095eaf6be0a44e681aae508aea83ce5aa3875f72821a8ce50086c91f08a731de573e52ba0a592b5f8e3d92200aa1b742f1c19533a4c321873ad2c4b5f5f24b2f6d0ae92a09108735c0a8795b476b92a55bb65a86fb142318f63bedd26cfc5 +fc0e95da298f47b4c5c200cb2cf6c8250297a1cce4de3a070884413f6403d2410277f934161acf154e2c2987279f1b8a1db936ab1232dcf00e02926a218388f972f4fd47a2299af22b9e15b9909154c0b615929f13fd058f331bd1d3f61291373027c965cf07ccbe762fa5249855ebbd897dff7c802bd81a7f89e5408b4a0a05 +fbb36d7d5e2598ffbd50b3ab673e91ceb8f99f6eb46a756396bd2189905426d492acc0eff5245cf16231795c3e3e88bb11b2f66c7c5995130f7679072addf04f7cbd9059755ec8c3163ee8390bcc1023aea55b16f85972c178c46600a5bd20c8aabeb7cfdc9731acb2068ab311b1665771d4b19a8d782fde357d9db1e6828095 +cf448a3d3b21c41ba1aee82c0d424c2ff6d5afa1bc2fa3c82483c0b60ca0ff0fb579cf87cde23daf83b1077f1333aa5f51dd14ef5d3c276cda122df9e79e4156e16f97279b054fda035f0292e0098ec65d9ef02f2c6076016969d58e1f9fb671e356a956eaef3b449088b2eb8443df2cdd4fb09874f6614328c41e5ac0b67e05 +d3f9360e3d6ea978fc7d18f492ac4696929fdec822744c17ed262748e6bac0547633d89bb9c4b34bbd385253f170f262a22ec37d0f68fec97a193b23d73a649177864d08504e9f952c5d52d831b0fb7a2444db147c2f1c4d5d359deaa19b5b335b968d713cedc0d02dd7f08537bf3f51e17ccc440336a242195c239f5e558945 +c72746e4ec4cfc545d4cd3642a4775a04fa97b2e502f52783f56f90e7549b82655053833afbee2472fa7fce36161b90b8463c22511122e63f9416e075517e917b475d6c7c66bb61dfb1a82391929213e05127a704dc4355b563ba246819c48bb8ca27cecb6633aa89d8114e8c23cdb470cec30ead9f9e815809e7cd5bdc51f0d +d2e429c50f73236e0eacf38428572263d1e37d3ca438d9a4738660097c26f9fb7f4c5a28cfa789465a5961c8a81eb6ec5ccecafe1022fbb5bb1c7bccd93be66f1058ce0fbe48c50d961744f507cf4236b4cf5aac01159c634d831941072e0ee1509c78bec8a8d8867273a5d59cc772343c9afdb86e6a50a8cb3bf1e65be1f405 +e0c9f261bcee9f861d84c726717f65582c17a3db29d18e1a5e4b3e5cdf7cb850de9e9cd663c2a83a5ea9282e255c6003b77eaa0cffcbf0ea1d01c3b9eb52aa0358cdb52ef08af713e077a7e4afe78323ad1e0e911209d3e9eb23cf1b4f9fba7952f697e057a2ef7905bd6988c7c669c317dfc2e5f960eb1b5c645e1ae9b6bf0d +c62c8b51ad3c9e9acdd33a4f4ea308e6cd444a539af5cda31c40a39769d2bafbb632f60272615ff1d5829849ee087f0351f7ea347fa6984532dc286ff1932871c7d11dfc6050a02633b3782c2a0c2ceb219628f6a638d14d8e438908ddaf6c623bb4c69a4a5cd313cc8d17126bba96e6ec92e501d8e81b836653152b3c3a9ead +eb161956621add40a0cc0ca3f3bc9a60f2b0f6a2274f4038213e3f0d288dcc5066ab3813fb42687196ab29d76f1cbfb0b0be7cb9606207a7732817a49230b60b9de7fb8e77a20815b83f84850dc0586025e12223f82823d6daa6efb34b8213c2c439628e81850110815d225305e84165f1502239ea79544d7dc322e69aedf2a5 +c9b557ebd4b4ded4aaa97bf4db989dcbca6a741d59cca239ad8b0121c35508f33a321a1fbb9123d9ba370ab69c70a4032f34c32af2d6a54d7e312840171ac11cd663b5ad8df1d7bf6f9bf972155509c2beeb1c65a0a7b85a857a66bfcca4a62cdb4156a460e2d6274e8589ad83431f824eb51b3f06522e52f7c2d3e93c541b9d +f17c93e94c7ab2f777f518ed702a02b4b527c8d21226a7941559475beba02d7e8271bd3b79b78ef5256b094610e4345fe43802210d2e9d072201436933fcf3243323b7b708f93ef223f2eee56023307c0206979876772ff1c302d9684def77c5c37beac1ba407f1c92f23df7e380c4b3dad03b333f145293737e48c6e4c29d2d +d677d7f7b547fe2d6e89c506500885bb6000af7b109349faf06cd588a829ff62b5e5f72da766cb26f49c41e40fd743f03b887998ab0dc0d691fc16073066e3a7d3bb562d25a109ef7228849be098bc41a94d1635d61d0c07de82649b597eb53384b4008cbfc981cd2ad32ce65fcae288c6b57e8410ea504e68d169e3a93ad3ad +d2738f6224131eaa57bac0cb592b5888cf9ab32c5ca3ac603f00dcb7523bc766033cca5ed14e1e85acb26e537b1506bd9ede1169b6771cab017e4b65fa473290a2786ddb0c065dd9e13d3fc3274c280f7cb99f143a3e2f3e66d3d259c9100c7852aafe78aa36b33545241a422734d1cf216c45a1316b7f5280656a768e8a5f45 +ec9e5721d7fa361c5555ae29c7c53c91735b9b9cf971dfa5359f7a7684ef737018b70235d9f856766fe15c87829fc13a0cb22afecb906af9a3d098673154121ae0556e17e94b05784ba4c3b7c0adcbbe8c595c4cab7c82c4c8dfa9f79c15123af3d9c0862dd46244ee60cce7861bcbecf41e1b1d39a708135bf042615adf3b5d +e61ca49b6676e6a5a59d07f5fc51b85ece0a7ab69f0a851f71688f3aca1bbd19b4cb9144869f8827dca9cbfffc59e57cd8729bbd313b8c1713598a584bd9d2f4027c6f88e24bea6738d07b5adbf8c0f38785aa33b6666664607acc77f4f3fbd350d0bb51da7d2693f1fb35d0c61c269ee5f50414b1a4441f72d97d429a6f4845 +f1ba2f7d8f063535c811c0b268d314f93340ea7c3d418928cd5016a2e8224ada2b4228959e6abe7c5c3c9e2472a5cf5ec6d21b7449af0bfcb00331dc64c03be20d75f7b29d2d47642792bc9acdf2f1647ad453fb3e63301385dadc8472048fc73a32e7d3fb6283994a322f7ff6cb8a419886c2c0f700ae704f34647a97daa8dd +ebf53f293fae2b16f3d0a34227556cc536f7f83542a627b250a4b6e8e41fe8a7a68fb9beeef8ed233bed0ed4419eb832a6e698d0ff6b7fb95617e2ccce0561a2b0f124b15bea9b59aaf86ec04374d7942bc6a7e0167e5eeb360fd6002fca0dfc7935a8ba069d47b74821777900ed205980e1b8182792dabeb5858e8e2f0c35d5 +c6894fe616cebe6bd407afcfd26e4ff2ce32f253bf530de41ecb645f181806cc3d592a18024d67ddfc1779788b80dea06c7c4c29ce0294a1dcdc76a30f173daa97c55a9f34aa0cb8f5d848a5f2f706cef4eb515397d4eb38d493a3df2b582d7644c3902b780c93f0c5ded25c50fdcaef8358139741af17241ff861f19a5cda05 +c044d81f9543061edcaac544ecff31ceeaccac638f14129d9df54c34891a9c2482766445a37e405493467ed188b570ed1e8450e6750873170f4ee3c9c29a519b0f17bb935cc74fa6cfa583f6891cd935cb8868068b5d76975074ca7d4c6f2cc0156a93e25c5eeb03d5fd6d7858b599f15fd8ab9e6b5dc22ad1a302f96775cdd5 +d9ac0da9f7b0cefde928331c9edd485728a4e4238bcd2c4df7c1ceef777df025a6c94c27ccdd49476ebfdf8dae75603fafb4563ebb956a6b73b381dec4d414d6031d3db06360ee425b7f104e14b993acf1594695500c2ef559bf9f569e3e33232c5333814d1060c696b2c0c58ffff825cd92ad1c29bdd4e0ae4b711639bb6ac5 +f2a92c73a5decd8e1e36eb832029d8828ff031b759d366267cfba24e35f01b45c637d7727322e60c27d55e65871362410c3a783decca9e65e821d5a21cdb8c5d97e13cc11aea383fa8f26b602e3a9d895ac042df6c2bf9d42bbfa6d11959cd9b9ea9d5202a857ae0b3c04b7097428f3291a50e86d07085e24b061972a0fafd45 +cabbe2b78d742f7d8fafcfc03d8d1c5464c5379c6a34514b255add1babd0fbb955b3280f9c9b9ee22a6139fdaccb09f1bef5f64058b9cb24aaa23b4bf88a7f34e3601ebfcd12ebd497a70c673c58b303e960be452f19b397b91e2aac835c7cc395ca2b0484272d9cea2476be8febfde504fc9cc392ae1cb89e464fa226f3eb55 +d9f356c3beb34dd9d2577d5cfc5dd9d7571cf90dfa1302ebb0bcfc194de5185237457e7324033655050e22b976a45fc5d54b5b22c0d50ac7762b6d812c5f32d682624b07e71eaf335c85205606475cf9206fef4c5a65e1c90cf4bfa74a136f592878410aa2784d0b30373067c2bb529ebd24f1c50e941137d244b83d903f586d +f2d4f9085480253f2946c7f41189ad8351cb734f6219905cb6f75537d6b505a49c350b5af52386bc59bb946ae662690fc349e18d31f70084b72d981fe557a70b2ebeebdb16a25d63dd93bfe9ac1c2a919ceb92081d7f47f5db53f9a66e5907c83201e41028544d14b1524516fdfdc1effb6741d79621ec127545ab4113d12b65 +ebdedff63552b39b84b4435c4825a88b523de9c880e30afbac8922f6a296e00ff02296fe3b66583c7848daee7b2fb7acea9144d7a5d3b7d2ca7c5e109584d08426069bf599fe097272e0dbe536caf767f0520b25ea5f678a255d3efff629aca6435a296f7cf6d29bb4c37372701c37ea7a952d483dd0d54c45ce6e7fe61e1d45 +f8932edaad8028dfdc82072d6ad44c32ef59fd388b6573848eef6239700a431e11ecffd167470a2d4d05f2e6522d69009b6883f55baac1b894039b13b98d6b5ea25bdf219c36040964551842c564b8d13ebd4b729d8156636bb7fc0a7965462b5b3c80786ec10827533be07445a6f66e54ade6155bd008f710e8dbe69cdc6365 +d143338d085ed0955e617ac2758b534a1fa75838e0f10bdf4192142c3c47180ccbcda159813a69c9f6db92a9278285315df6b65757d42a2a1555bd4ba3477dc65a9acad854a6fe0b76089ee6d7b76143fa08a67a9e98927fca7212ec94488318b291858f4fac01171bf595626dce76ef81ac84cd58602a43770a71945e41f20d +c7ff71337de97e9bcf2e125b1731a7ff3c139e4e999a9992c4d9b4893f4756d94507a366a6d8d435b5f4b8618a36cefbedecd4fb9d2c2441dc47b085b89686de53d9a1a7e673546e94db1315bc8369e3866965f55200390575a1dbc4e1e8b06d800068254a21d54c6d79bed40e25b625f6a2fe012635c1c7fee7f60e49582015 +f40213d83153dcb0b019d5ced1f4b3e944881f6638c5f325d8ed05a4c8ca5a6ce38815f336610ba4ca4c7337f15ccea090478c97f8db9231ba73af7689434fbc6ec171944dd77908cd0d329a306957079b6401f0f34738791093d71fbbe9f7af7e6945f7a0c8806abc31f6cffce47c9d539f3f4cb9fce3b0ca54d1177285f145 +efb3b6ac061fcd4bb6411ab78ee10cca7c14b5b9dbd07109d1f6e03b5ef11f4726a88f89a5e79e66a11d59e2ebd426b3f21b34e25602adddcdc87753702b1e4b852a565525559281d32a753e363a98723f365c479dc9f1dea8311d723dc016e811e0d8e918b63c3e6c3e17f4c9a765fec6c563fc1ab1ccd5c916273da3a0ba1d +d0b8a638098319ed6f0c0bc2c0e23c36b000059c0883a8fcdfe9357f4541cfa6f06d0355b0092107459a752b040722356d7f95320d7206deeee6dca587a60acc2c64d8b63067ca8ac10d221a6ae7311a083d62d180f9524cfbb7f5b3fe71f0e82bcc29b64d55872eb03b816af1d23a54d6fc4e395764d0dbdad63e494b16b5b5 +ed0752e6c489b4e37b27a1a90073f5958a7eb7dc6be8c5fbabae963784ea0993152cb9b8e0c9555fde1cda14f4e377611e66a7e7f50166a6eb0e37286a08584d931f47eb66dbb234c7e10a5080ad84edf57765b0e2c32eae6c725cd95932e94f1475a1241d1e3f8b5f11c0ad8f399332a0c1f0a54f48f81455920ce94a3d18f5 +f2164f284acd56b67aaf1b0cd0e733709c7ada139cdd348c91eeb742303f96ea21d3f4b423326ba8cd95fd1c36d5f99966e1dfb075fb13ffae9b2a37db11824a6e184980ff63322a336cbe1cedb8c748349dea7673689c4b34be89d065441eed9b010c540826a2079346eb90d80bb219c973718670ff8803afe28feab3019a0d +f9a00b6f7d7638db994b087390ba2b91cca6a3c30682898efd42f300aa20116e0bad9392294b06fee445d694bc8a5d93e4bffd7f4deef45660d39148230b955b0157bf62255e31861f9ee002e78c300a8d93c11f3d3d7bdfeefdc87231c9001fb7cd41387153dcc91f8a1a1663718494d9d6c866af82c0df6b796943ddce0f1d +d77482d83a5059204beacc0c1dea0a87347b5dc9e5dd04148a9339067121e4be8d8f3bd13a0780e6efdcc077be7f1281df3994031ce308a1bac1024144dde9e3587984dc76b919e941abe1b9e6a6c83076389e05b209966b698942f02e83861a30349797354f02d3a7d1fc0797a38e4ca94aab692e9d242f8d11d8e9ceb36e65 +f36609d7add6e2e5af768e8806280de76a92799a44ba2317d16b35d8120480ab7beb4550c706984300fce781bcfaaa7f34f4b8313ba9e350e35136e7dfe060191dffc4f60803f87a23e70e78ad4067a6ca1ceec7605ec4224d9e4056322fa932fc8d86c3eba6904028ff1391918c0628f23425369788e6f15c5237f3e945748d +f76aca3ae95e24732d780671dae23eb76e95320b999bc3087b9fd5b47ed48ac45f276df913fa29ebc0f267c5d6149f5ef3e995252ab855a75ff13294cae22463caad196c29593233bd345c470f48ec0a33855caf026b02c7e9be7585e6a2620ac8071712520903ba6ed56af7633e08de22b65c24260996b905bc7b17305b6315 +ee47144c7cc79048da2e5c7ca377d6bf54373b0235788f6515a4d05d76427797e8ee951f3880d80105a8b9a5e584a09d7a278f9927737c3cb3e65b00d166696d1e299a81d0b63b3d16c2e80ef0769ef0d49219b97d88772147bf19c8e0e2354dfe0753613ffdf82ca10b79a606969a7d10ba6ef12d41cc9ef7a0ab41a60aee05 +c7512a6fc248a703f04a4628a04c5f42925d0b3a0bbd26671ca332a46ed23a23552581ca3b14fb93c50f2b34f27a4ce81b7b32469a30a02044b08e1d8c76d48da3058e821978bce032f5f8cc59d85d30ed8f8cec79c716aaa577e1c0f61afba873d6f8709695f4bbd22241886fd684db8f7371947330c16dc5cf588393011035 +f3965870c5a0eb8f3f06393b00ff005947a4c93ca91234a60e0acd6174533882abc26e4c4660f194a8851b59029bc31ee4e696fa1fa59e7c88ea0a2b91960d4146ad140286b1f3d3727d0f67b29394d00a604e65e4240b9c93c735f665e1cfdc69cd48a2dac10a6c7b5140d8a57ad1841d99ac5631fceaaddd8d6578b4ea8ead +c108f896edb6a310f4b3acf52348f351a213a75eadbee769f484a220fee6d32fa5ef491f9d39d964015a4eb6f741abd14914cf03ef05f039b0eb0361c1dad1836dbaa575b6b9cf69d3d39c15f3fb5e1c5ec54a80e49636096ab75916e269b1a15cfc5cd374a064a3251b5d6cd6de2aa65aed83cf68d4dbe1ab9682e98bad22ed +c47513e74cbb89be9ce2f69b924430b0f84c0bf6b99414e7db74ac0db1d31100d5f06bf3310e11d1feee623c4998d40dddb72481c45eb87ef0e78b71b85aa1bc121613106dd4fc055a456dfc0a708c5c13337214aec0bff64bce84a49751451ae207b393b2299713cb705bbe69b11b3dde3f818d3fbd8293b501d027b2c2cd7d +e7c4a401decffc41ac44a48dbd92dad6142e816439b07a93c64b0b3eb772d278aac72a5583b93be6d23c85c91a22478b7ae1e9544b05747d5003809f17f89593153595cc572f9ed5815302e80c5eca2a360f030ad556787222cacad104d6c20514be1ff92047b6d194c5706c95e1c5719d25fdfa818198879d9192405b546945 +cf1924b43aa3edd753df6dbe5eb4453194ad50937d6072f449185a6f704dff7f70a28e317e0c5e8c8d67164e83df9ef57eb748b38c4a08248d56e7ec06caf4c47ef7bf09d291fe57c9b0093bbbd36c323f383170e4d1430012eadebd912ec07c507f2b91de125d7711869e7311a98f4a3e63c37cbf770fbb8f965c8d8f337135 +f8aafc8ab799051e91d03ebc27f3126b952aefbdeeec0957c60335325694fcab82a7b5ae5807699479851cc4c5c50610fceec008da4a5591a5065379db44cc33e3d8d654442cf7d8867973a5b7a967df6005192305bbbd556ceb2f51bd80115ca86fd299b9c01e51e6f7467f1dd6decda49ae2211fbc8c1764cdac271290316d +dce1b066d202d9d4ec6be321dbe4a0f27be678cbd16d30bdbf55877c70d7ef875fc895611b7c5988e1d2c5311b4885b3a8845186ac8117c17d9302b66fd1de0855058eae06c04a1d6ed84facea5d5cbb88cf5a8e7790aea33619a681858cfed59a7cb1db54f2d865c71556d4b9cdbd8539015f00e264c0bc4e4570eada2444c5 +e9b694930c95ec62abdba4da1bf14713c02cda478d380e6c4cf3e3a046fad0807211fd0c7f1f0511a28c83cdfbc56f48abe9daaf48b10433989ceb489aada52f35d23aa25b8ca027f1fcdc8457afc56b177aeb04bcab67146ebe9b2ab1e8000a8468f112c07db377bffeb6ad81084001819a8e1bf7360126e344105eaa6c22ad +f2b747825bea0eb69671e2ec7894a0445206269d9b04a1fbf7253ef4a69c05a9ed168408962ac47d852c85cf863534d3ae082e2fe90a9daeab63a37dace95513525731ec16f629465b9f7f05449722fa06c66e6c8b8a909344cf2d4036b5bcf044fd7dcbc50eb53cf2a1246bcb564f8286dbd4c6e8ca155152fdd1b1627b71c5 +e1e43a3d617669dff0cd6d191f3c92516435c17e5b3d8ecdcb57182b7c8529d7def527c14f660d63e61bf30b953f187ef06e378b2139ad0f30f4dc6193a3df84eb265f4578dde745ef8fa1f10a3b7b920bfad2a113452a222b8434d4000d53e52d8e00c3a59f9d7e4514ed175101559c636e90a1daa5d7094db4c529a0964145 +f1fdc775e8fc8246bb2da1d922e140782d2f4ffe317226701bcdd3bbe97efb8197f2a4c3b9e59d8d489eec1eef8bb2e39466a9800b5b112d6d8cd8c55eef993a6cc13a8f9fd23ff53cc12de466e24a033674cfcebe7baf90c3c3f1fc366f3c463fccc4c02dd074b1e5f83423e4e26fba1d35d6b90f22f0369e4fa35b4b46fc0d +ff0bc4dabe703216d2597fec46f08827f09d8277baecd636cb7e63c08004849a4baa21ded34362a871db6afed09e0acfc73cb36220bc5ed0489b022e1e2491eafb0f44c96bc7581ea02d841179f4079370b4dc576357723496fe7e0217dc8773fbdd09b562146613fddc8b38ebf33e283b9f3f5bd17bad87b60d34a191b9cd45 +e4d00abe3070d6233ad696bba8c4ec62f9d3b372a799303bdc4bda030d78db01cdba42919778bc3ff5703fb14798ed26c182cd29852d2488a9f72ecb2e95a7241df1f8f3372ee7990460bdab916884e47ba8ebbbd5eb010bd36b267fc0be5075dfaa772853187e02bcaa6664dc3e98cb1744a47b15e6a92352377ffd5a7b277d +dfcc6a945099cc5fc8a3c59d70e46e29acfade7843ca12f82715f5eeb8975a379afce84de3cfc32840391464413667d6c6427ce85d26fd9a737c5aebf4caa6d1419c2f52f661eaa3a05642d575f855a1650924a0b9adf410402a27f2d896907cc674c16c04c36a385b951d885608cda14a16d6905de193f7e9afaace19a26f25 +f756cf4396de4683afb81f0b1be7b72fe5304e807c430bd35dc598846a3c78a0546154c4798a43c15916f1ff06467bc4d108ea1a339f561d728c1a4e0900fb7f22da26729f5f4093ed53e44696955ff94c2211359aab0d7771a327c69ee934f31db90b4bb6c85c805abbde0feb40f6c26f9d14a496589ba1e42bf86dba5ca025 +cd3ec9b8f738386ad4b07a2411e57ecd743b145b6e865b28bb78e053be310aa7d4ec30e375ba5d2be7cc03719984c05ad512d0bd1f1e41237c1205db19f933c4c3f28135a7935b38e8bae7bcc5bb3fc8810e59a1438286b17189b6feb5dccf584b883a42c14127dcfca06e4c7a0249ff4bcd28c78b7a0b4f608de3a4fb13713d +d1c3b913cc9a0dc1c63b5da8442cb55e508a9fb81fa5538bcda09696fe73fddcdeb2b9766a60c6fe13b6b5ba25cd99414a29bcf87bea3225dcb90c9e5c05e45c982c60e36af6630d023809d0c9c68d7ec1d78088722be1dc45204a02be07f7a33f3688aa0efdd486de336a6fe9d40b24ae560ee94329995c67dc43c999eb584d +e6906f19fbe0a7b73a6602c14cb916c205e117d109800b7dd3c3c0947c65a58cb7933544b2d9fe95defc58fc514c3b7b7e661549223c407a66d8293d895ed71224dcc7c9928867c0179719421ccf1432f01481e76c79ae0c5d5b9b5c4c43d9a903df053da80aa6a2297e7727f8d263f18052bbb4691e7bc2679a4ed9b1655485 +ffaaceb134150a10384cf039e8ad64bff0d2f34e26d5f69ff0440b0d27f9964fffbdfe722279ab31e4ec029394f692dcf8701edaba8b577d0ab28e217e60e2ae885ca72b41624ea3c0793eb388e7c1d513d4c0169606a9d030cc397e8dc3cf0e59dabae23c06de3651ebe395159554f0b6b861bb22c7b0bae083936ca238f43d +e1353b6edcca52bb2e5f4856c771853d534cb7be128ad0a62fff102cc1ea0c83f143afecdc865fc72b1294a940ad4cb7a0316c255f87ba7dc848e7a6a81cf339d085d64002815434431a53c2754e59a8b9cbe97f9cb4321cd60ac9fdd828ae9ded8954efb7287dc088212f66037e5fb80de89a60a5c5daecc91c371295a2214d +e2f3e64e2b74da49aa0993acf7fbdc551d092a9bb47b9c9d5243af4bf4ca6c0e0fb7b7cb743acc4199b9829b6eb893818bd6f60c40f88095efbed69aeaa877fd59e9780d68e2ae38e5389334db71722adcb041d4bd31b009d7ea342e2964a31ce795dbb22b62a6aba4e50edd711db57d3573d96f6d40a5a0eebf5f7fc041e665 +f05bc3e8dd7a81fd90afff645e6f99124219ef8358f125b4deee68652446147c2af8a6eaa2527d9a62755f5c663b936792d052ade822b93f2aa328cedebc9fd32b787a5ac88cc439294672ef61fabb0aacb5a8a33073a6bf8665cededa4fdf80364e9a38c4f2c4a348cae5c7e29bee6c49655dc6a705c3881c7e408aa5385725 +c0d865f35d27caebe727aa38ddd5f74bfa94d6a194d92123ce6a2d939bc9208d4cb9222de08b0515bbad5ced083ff087e516d2e6c3b37755311d233c6cd1b4e7273cc800e5704ffdd28edb92b781143f1f3d259a1524a2762e4d6ec13c7918f54766e63f62305b5180d904a7a648ddf18ac448592855575a0ef5797cf47f893d +f032c386d6dfdbdfd28ac2006ace6ab1591adc464269b33f27ac5138ba399226c151e9e59894d48b0e94ef366884457baa930075b179c7bfb61cd1a452b9149e9fb18dc937df8756d55b3c8f4c2b5419cf54854d3df18df3769eb6b7f0926764952ef7f453b460941cde1b868be3ec8cd6aa333b3a8b45bfdfdadc3bdb59b3d5 +da1915e7c7e36ac52d28383cbc60036ef18185dc10eb99a72cd55b819cf5640bc3ed899ae849a9e033634c9ae84e8ee24359c2502e11ffe142748238a2e57fa08c46b43ea52742eac29564b598f32da59ae9fbcdb34fdc30d3e1fbf3b08d0076344c8eca5646bdfd787868b27fc615a93de698497580586f41cee473f9309425 +cd2946b5bc0b7e1289bb27fb6e81afbf0fef4300f5d0317445419bf9aaf4835b9e6d4c6c10ea746406e45942bd697a8147887900bb31d42e18cbf29bba283786526823540d959252f74a3a6ea9d5561f66e9ac0cadc457e46126fd3b99dee5e4252449c4121722b19bd01d7eb6811f705487ed1d4416a274798b05d5fbac5955 +ce5a745d1e79d7b335d7c88377b1c692ab802a6665bde0af07b37d70e982e0c1af32125c8af9c9cfe22e872bb4e3a71ddc49d07ee17db5bf045995f3b6f4d79da9271d51b89bb13f23592cb57ed01eba6c40949a967adb0f8e3606782e9866322f3477043ec8b65d6718bea34aa025e8f08eb0a8468050274a99bd7f05711ad5 +ff5d660485911741bf3cc5f0c060444c1c847a17c2d7b68b40b644d944310ae3a4dbe878131bc6685242ecc37912cfa7cdc6f4611f7b9873ea0e75c59cb7da908d5c1d503a1720f67bbd2a15f3b035b4e89f39383da64eff98f34bce4f3ad3a91556c39986ef56e94cb7442df4f2687fa2dc965011cf0314f9fd0a4e99f8a82d +d92d8e5d030306cbb0b4e7145a8a4e0636282a5d7f29816cc37366e95d5a9f51415539448edbc6903b07f0d81b84d5a8a33c90f7b9e4e7df1e10bffd107dec6d9362dc86513051ed064f8ff64f156903d0a96fde60811447570cecef2a77cfbb9ea1e3e43ea5d703d39fdf0e3720338f7eb201641fd0c63249d97bb34227ad75 +e046388255096cdd05bcbd3153f97722a6a7841b7c49f9c13542713b5909ec25cc2da599b1025e224b26930b5aabd4bd2e554ba3bfebd531c9586f38d317df4b6c6382b67c99bbca920e7592fdde8934cd38c07d3710be51e1ea08766709a2d63a8562a079ba6116265431cebf8cca528fd82fc77f0a2de81f6e8296fe0715b5 +f5ac18e52f951d10be9300cc6f88abb46f237d7c0bdeb4a088e9352d8fcf1dc59c5b54751bf27b2136eb9e7703d70e4287e5cbd5d07848ec3bb116d977938bfbe288d85e98b68cc1c00a15383cd01f6324f0d78b44500bbe68b27b32e19778a165352214eaa2156780deeec3274ece0ddc7f93fe11401bd49e915ccb664a3f55 +ebb41e1f5442da75e42bf4b8d1dbaec10e59b71b126a5825f03707a9608512ea068436c3afa102a4d2044ea7e6a15e70d5726d8f68f9bdc79f09abc5a2df6946088467b35085fb626a1be703441ccf627eb9b3f56e07d932e8c4b1ad3b2ff399de007cbf72017d819c4ec8ba10daeb75ee00c74bdfc11ccab597352a145c88c5 +f695a2a2b05e885de58d21acc17e7d0acec99cc82a40a0e65277cbbb12698e1101b7d0e30828a9cef39a3661e2533399a754cc67f4f927e084d0c6e756f0daa1e53f1656d637a795972a3d9e65b8e76d5a9556bbecf8243b6f5c56858a0b5187248630a6ede79b202ed70111da94a25f8e95abec93672f1650e9f94ad8e8dea5 +fb1965abe9d6b95979793175667a8174540dd9106ca08aa16da6ee91438ddb51ac0be3f88ac43b3ff3b3a8fc72ea00d669ca2424040f995a63c21fdd6806883e36bf88f7cde516afc79cd2ab91a030c9fcdf8f043cdd33e448178a37f5a5c75fa3eecdf93c24b97a75a09b07d8b6cab8d81338e1155a31305e60bc7bac49a9b5 +d08191f8d2eccdf966eea365f8eff23790b197673dfaf8727de20239e5e64acaeda10dbb9d662df04056a6d7dccb35b4eb78187ffd9b7625baaa1cfdcfa7801a19e63f5353407f5628f10f1dec5c8a02b5cd51cdaa3320ece9daa1af982899280d6942fd90388b819c3f3550974814fd336785889f966a751754054d31eca70d +cb6ae70226b42cdceeace435730d84c2e0a62021315a08f8c91746c6eb4ceb5705bc1344ca936d225d6a8adb1fd6f9ce2249a16bfffb77655d314ea4daabc1a90be9641b787847430f03f1f13b2ad301d8007749a0333ec9fbc233d7adc597331614e7789e0665676aa1eb02aa6fa1846e1570332943f304f96f4a9d467e4885 +e91ae993af41925b63076c8d6f97c71f54802d64ebaa90385676474c25c360084080594c4ec8fa2db7436a7b18b55af2c7902c4cadc0474a7fbc2e82783af9bc87eb88792a0a4241c4b99383585225f1ef1c7e46bbb32c8ed90e83cd2395688821610194e6bdb38d6fbf53870ac9c20df991227c598b300eb7955f7a44d6abdd +c865501f45da50c7061abf8421dae04745125764a6fbad33fef595783dd8676bad62c7ef7853e4b75f4b5aab8bab93b43999a425a83f7176199aada5347567b70d514d2c984bab627c8a3db82f29a0ce1f7d70c1b0de249689c7c59e279ab94c5dbe149125f79196660813b7ebcce27044e8429afd6be018c70a49cce6d76d9d +dd8abe13a5ea14ffd8ff45ab9a3a35b90e6bc79c018fd1a1005cfb7c721601ece24f2078b2289a6afd9bc3d2c63a7e5c7623051bc4ddc8bd9aabc48b7b5d2b5ca86fbfa6b8c980b37e7d46bab3734887543934bdb0a75eff82bf238999a51cc1ad0595805dc27068f8d23af344e022a916bd2012fc7038f547f92d97f76112c5 +ec32ef777c9318c52ea24f7dbd1f323cdfbc1fc91f8ef39248190d4944d320ac758fffcd5a7ac3df78761b1a27cc90da44882672496ded73f2998e4884a86ec264a2f368a6adcc210bb28a9ff66358f15c5e531044a71a7515e7c17d00758b289ab786acf07860e0de22deb357fd45d2f1bc289277456cd028da8cac1f0252e5 +e1e11019dfb7beb92187a908847bfa04cc987466e9f709f3fca57d32699d961cd6630a147ddfa228c30e5c5b6e75d9badf16bcc6edb5284edb255c64c6af7f977c4ccc5893a10b621c98a77eef18882a25852b4667dc6cb03a09a2dda391fff31548f4abaa33ea5903ac891d907fd2e2f56706ffe4bf6eee1e01cefe431e956d +fa197e6f7fda770e2db0630a8d13e4ff7154557470fab3723ef0c7af17974bbeabfa5fd67234c92790f3806e65cc938925aea2eaf11a27535053781166b8c5e9b8edb8071eb0dc434a49bd78b4d36557421f0778c122716598ae748051d2496c518c3fe07fa70731bb34186e6c26c1fbf3f927cfe3a6338386aae4f109df3ced +ee1d856fb6c508481be35e01355c6c32f4fd918834444c48f7dde2b60216389ccf46a42e5961cc40e6cda7b5ba2235669c466d6f14950055c84b339ca92693243b9b7e02a3c105cd6a25ac002d3c6182e90661a913ced8678c231e4105163118c2e719c5d5fd8665981d7c842c282944b81bd482cd9c4f1879442994c62d38ed +cd1e3cd21cb756d928c0f53688aac4c03ff7e71ef20dcd4798159fd8af9a105e5424a23c800a70de73cb5b22ba328dd799c85ddbd20c62bcb4b028ee1476dccc6eb26b932c12dd584f8fb6d70f4046a001e72161db28e2fc76cd5317edb4b35f7c878637b7a2f1788395fd5c019a0033c7c9caaeab1700d89deff335f5f65dcd +c6c7888d3182c721e7989d341dffd18da609b2578e48825acf5d541e54475631b5de037f8323981a7d6ed462c6add59fdc2bf323c9d1c502afd881d817b6a9acc6682cd5d1eb0f7d7cc607580703e3bafdedef36b7af7afedad4262cec7a345748b237804f6012f860dd71a25340d5674d1e599a4dd41b990f7d504195f7b0ed +da9a9b87449827f325fcb2c03bb69a87074b2b97434e03efffa97e3198e3e720d806fd0f5767c29340fd2b8d8a7b2a1df39327de79d848d600b78cc4be447c6110cb060281b891b6811ee411a92ac23b1b19b224669ca3512f85b6e69aca9405ebaf5fcda241ca7ffbf7cc1dd8984a2670b696d5d15aa3846d919552208f889d +d2fad86c78c301373441fb2ca6b8dbe2bd3088ae6f9351df1659f2a7491a203aba7ae0cd4c7c9747cffdba58b901680df5eba4a2dc9311f25fd1b48f4464b55b239e5f6fd26bed5a636b73ffb1df7fb7160d065492c6f2f8db8682aaff9d4b5e9969f0817bcd123be3bf3a2e1de81cee2b0fd7bcddc7fe6c652e6e7e9e3ce07d +fdafb1e55ea0bc6bb0bacc5cf0c617b39f41ad7a2c7f760b26e963b03b1cff39a7a00ece74c36d6a964919430b0cd5f2f84c86595fb34f6c3be57ac13a985083f312d61784d966f82c28a68f4f810c2c7b27d492355f88cd9b47baed0bf6fddf5009ce95576c7b6d8a718de2444b7263ac4a1bb627fe66fa797ab27aebc9121d +ce606360ad461a06a31c1995ab7d67944dec0459c06c95b0f736020864d80511bc41e62f6e58274a3a1f11d372e0f39aa23141979028173f1778eb015c84ac3a9ba7aa492bd589b16a2fcd50288d5e34609aa84a0796fc6165a0684e7f12acd722be221b226a83c9565c54eb1df3dac8d987df544fc177b9981e617e3c160d0d +e0f7089df05ba237db3b1c45bc21d66c29ccb507e9daf95001c416827fbb671390c97df7b83788e2569a5c5fbfb6843cc695f239f5a1e95303dc43eb5b6af0f1c68ab9e90bcfd516e6d6b6ee40d4f9ba37b6c048525c80283488d70250c5cf99c971e66bfecf91de978dbfcb7c6ae04cb05ef016202f30860479ec1f90ad4d65 +e8507ab662b3c98ce61e0a121f721a55fd03f7ae5112227da37af43bffc91cd1467276191a561cbaba0e34c1f454f679f3999d41e3670135b1830cf637397c4a5da44e050f872e2f9ddf38796a72fce90591b01f2e2c5fe715617386044a8a971411027d3806d6a095581689065bee15a4fca04958fb5ddae0ebf5a300300b15 +ca0a12b96ffae3b5527115329f674292820ce3d4088c63224afd871f8119479a55dab101651ff8a18c3adf9c72636fe333c4b3ebe674efde597ff92af502146127990e7cf6edd2d26092c39a5c54bae54cbaf1bffc2a4694b50c1d85e5a2f787d5f9440626b3106ebb2e05bde7a68c2898285f37ee3a28e2e1c9b0d3a0c485d5 +e3e88bd18aaf97c07ef653e8fc0a34c84fd1253a3569bbd446dff4824c2603c7f8eb27875c696cbf4468ddbe68f05441ba1ef367a318e65dcbc1933e89386b188e530ecce528eaec744f90bfa84646c9ea20bfed636b3064b3af93c55e78c984e3bb6fc651bfed438ab861acc7122055624ebd4498833c744b33ffef8c00e1dd +f8456e31f6500650a486eb37443fc7952d67a1769c9fe245af454f76ea0b3c5e7a16977abd85af7d224430556114337e54c2edbe2b3c168b6d091ab6969f75eaf010eae3967f346cfef4fe37bdde5b945fa81c545bc60a44ca4cdb881cb1d85d7756f3106c868ef2554bad9fdf621b6ad576dc6502d3d8eb338a6c661e0b4575 +dbd011e8510af2bd21a8e5aebaf1081feb39a43579a91b1287d1855ca69b461bfb39ff789bf9e18d521a29f9c0a98f801022dfe2b48ab47211d1e2eac93507553f05bcf830b9c0cc01a82e23ac09ded672c36287ec331146d15837a5132464cfd9a9850198ef18685376343822e6bd00b0042b0fb9a10c87e2e6ea25ec08574d +c88a0d8e14d4aa0b128e0c7b39ee774e520293f1dab004456f11eb20c812d76568b4255db7f62a4d003bf2cfb2e1e7d5307fdd19a37045aba83e3903cc80783a40c5b1ef779fdbabcc4b317ebe478f89bc5097d4bff5181ce8ab1485f65ed28b740ab329b4b40f3503778531f64608798271f5b7449971aa01082b742ae821c5 +f027a59ef13a773a37c37ebed7c1f185e170ff69456875c8dc725b7294883480d264d53467a7a39a69aeea0096c69f7e13a48be0e18acf2d771937bf856e63e584dac3a764bbe19db2f72f17c15f24d32c069c3103b85bde4c3addf09b97d195c26ca32f1d0db60d1bb3e8ca124bc68cd4b347950b3572f794dcf2ed28827a05 +d0005617806e66cdf95899ad4657a55406d11afc7e9509e6c203f1fb0c1cab50cba705185bdab4f711f9aa6e357f91b20dcc908cbe10180167428ae569a9aca1ea17c66aae42e99076dd72f18eebec4071c1873f323cf6fff816845c25725c19d59a5248d680f85e487ffb0b50400853cf228fd8c5ca39888cc5791cedc9ad25 +d54b2a1f600724faeef022a75a803724389d6c3bfecc6263b54c53705d78e0344e0c6a9fb7d29f707c068adec28c17723225f0fba495f6ecd6b6a379c3472665beaa8152cf3ee69b619428f5e38c874bc9b648a5c00b844288a46c4996108d3ebf3c1f43b8cb327d04a59ea5c108e76117c487a9b82f93cf928738bce6570655 +c8284c1ab062a0d0ff59bcb818c10920ee12ecfc4c628791d690bd5aaa84a9cc172db8a77a6139640968953b11462225d2c980826a017fb1260569859a2938e2df25f5afbd789e8c2edf0acc71e5cb2cf082afdbc5b4a1f8262df03fac03a2b05f67ab51acf7fcbd9e48d07fe439dc14b0d082d44b0b761d76bfb17a7bb9b785 +ebdb28743e4d9393c2dccf2402595e183fc6888fda052ffd2cd7b6198f09be48f1377712f042a63eb1cfedd9580099ca32d4a61d32b5945557ef67d9950d195009a4195167338243c83cf66bb24086655741fa93c99d3ab51b36ad100e02299926a272d56ea1a1e9e3a5b6ffd8e8a64abf5416fb09d409193319abe153409d8d +d65eb3f296ca96032658f0770215e613015268edbf6c29a95809f565029fbf9bc198cd69d0cadfaaea3105914a0fb42e909108417b0c429eebd2dab7833a3a840dd477de79e8e0a293fbb0a852522075dc5136e6c11cbfb0b17d55c0a7efc5bfc72e4b4b388eec6aaa0d0faee88dfd94d5d894f1835646470eff1814bbf139e5 +c05e541401f5c14ebf63bd8b9d8fa9634438e7fb90abd491f818d98000e0ffbd3452f77ebc328e2be0449f41d2c6c5bdde140ff58dfd1574e9720395d703fe10ab0cab8bf3d3ee0bf1f733dc5fd34e0da04ca61dbb977a83e27f09b23686daa5f094adfb78651907dfbd1ce6a6f18c54d1556292e1f7f3e94d76147a89fc0f2d +ee4cdd912fa1bfbb9665235f0a06a7cba1a8419cdd12f8a994adb73f241e5bade29f12a1a1e4e81e5d3beb88a15ac2f38e8f55361355a777706812db82c73b6f332703a9f5a4fab39a3de4160b5db08ec30b7047b88091ed3c94140cb753eeff87303ecac29edd3ef2332a546c0b871cd1ded23f27b33852d5bc05fed9c34855 +e1064d22e161db40a96faa22fe4ba463b16a94d17307cdfce9d9c0b642c5d167f1caf1dd777d8623f8dd0a1e0beded6bd5944a0107b045cc4310e6550db18703179d67377af2736ae9ef32b8e625a31254213d2fb63c815a960f78c18cb678ef77aefc1dd3bec2a7bd3d2e976a76fa7a8a6514d1e5d18e55f3dfc09b9aed64ed +f6bf2537599214db59122973bfcec0e66a65341e7bd9fc04c24c9600956b6fc07477d954d1bca1c854469182c9b31e92f7d78179048cd423ffce6f9cbcc91a38b4af16ea03a8d15d2ff8e3412dd9427f9954ea20fbfdf1fcae506d5d732b14a2e0bee1d0f93668e71cd30ee6da165e77dc8935353063de550c8d72573b66f635 +cd6dfb24defa5b2b18cdef0eda2adf0e861869c77a9ef643e27d9d7a02f001e32fe9927c1068aa0b2ee6de142623b161158a02af1c5d49619453d69556bb8e4dbeb36aefd74cdfbe384924e4afff623e4f5ea764c41d65f2032ea5c15cbedf0beb93f852ad2181b4ba545ca1d87fa9d9fbc84d37ae6cc0270e38fabd4ad160bd +fec9e49cce336962d2617746705dfcdc39ddecc68edc64fd17ce44640aed8577882d1bacdcbd504647ecf987095d0a3dc4f5b8290141ac0ab7442b4bdfb78e80f3540a0b85946bf8496a0ad4bf0d204c3236524e6cc09ca68f23a341b31ae7bad457846b0a8c37d4b9e09f21c1db009dbafae366ac179b4d5885ba3f3486515d +f4cf8eb9f784a44c2acb080ad31a8cc9cbc2896ea56bdfee5796eb213b1c5c06538ae73f6b8f3542fcd35a890a247b4332d99a8d58ccc39c3d663cecf6b29cef3afbffbc58eccc3719e9413ae52631be34af052e851f28e5fba511e0c4dab5261e5efdeaf4c4de17c4cf2531963581e038c878b677a365300a377cc8a36c6e65 +c5aed2f3eaaa7996b9b4d936d94d5e73fe64bdaec3a3b2d569a59c030929d43ba23454e922c76bf3e122a0798f6120f87f35a5557ab785dc2afcec66fcae2bf2972872613b493c5b9628d6151ca970b6f8185025eb4d253be4b638d17600b5aee7661afa0e6f56dead3d2de75c6d4aa0c0e0bda945620b44df8cb41f05370ec5 +c101c7e5ac5a0c57db703d288230e7283bd8c9d46576411b6df847260851335e155e08770d9ef9c2ac27cd575860cb65fe85eaa89eabcecc69c0deb244b7fa824cb09763d151a23135bec7a26fde2107af92b10bd1994c65dd25ed9b433c382443987af8edb72527e1fcff85bc0a94ce6de34549ce57b2c74321ba3975a1c0fd +c8462781628fd6bb2c9f47406393ffdb3de02027d4bc6a4eb28ac3cb21140958cde30e5a6201ddfbb8a1f1d40789f439b295f46fe034e077969f895178324b9f23432053f19c0de921076739c9115b364f2c1ad3267b3281814d99b769b386b60f14a57dd47b4095e56b6f0e838467be9e42d208f5574f231acae151c2ff0cb5 +f37aa90dd2a3793e0089a89a0d26e05c38a73328a39e21bd94512b1d81a3804f180f98080230082cf34eac7a602d78c9e402e77a05eddfc6eae2a8ef68b219d93c21feff16e50cb51376ffdaa51b9a930a9b18528176db4ec0c0fc2a535a0eb2f82eb5a0c5b8928f199458796ca0d7b2541a275895c26048dee1f7ea89d61d15 +e47f1e715bd6328113e2353bfc797cb1a008bc12910747730ba17e7e3978763485a68d8a6ff3d116c89cf81bfbfb00bb8517d382f841089a688d33bfeb8ac83d6269ede3d04f831cd65fc9e176c5295b488cf4571e1269a33dc41b1f079b7c0c5981733d878a5785fb261779f529d09d6ce90e94988ac4e4308aa25734ba08ad +f3b54bb99708acbd6ad66450e54026cf28a3c958eb019bc18f429ce68c06afbd52f63f9769e5897718564aaff8e4771524badcfce709a67c6350b178f436f48bd5dd546f3de5d9cd2b68e0c9ede5e9f474703d1956e7d95af0c8bb8d75568b200acf5f6717193ae70c7646676481af3dc2be3c777e1932915f7d5614d0af1e45 +cc59d09c80ee1962acdbaa0443945b0120ea91d7b95ef6ed0d325df83a8c8efe9b022ca8670af6fca2e9f2f0d0454e6b5430a8233709b2a4611163ea979f14634341446b8be00f4dc7b1b6920db2cfbeb1a75861f3d38b373b03a35ec8270bafec241f226dec940003b52e47489835e46a79360c963363aa862e5a316cbe3bb5 +de3f7519b2e3a2538cd66e3b2ba8f5ee8e67961e89a5560f1e89a8576f425dd8c89ad8a74ff176d76483b75b91ecbde59764e9d477d051374e9276e8bf16e132b66b5f33ad93bdba3d3ce14957db94482bc0a1035be41ebed25002e0df2dea3ba33f0127be27eba5c425d1388f81a6b9d52f7393e51abb202a04a118fc9d6685 +fc107d5a74aae7288ceea89bc44c366784a36461f2af8005d1ff880089fd7190f5986de041a583341d8ff7aefa3da09bd36ed24c2ffecf1d0951ee1b34ebe1cf737e4836daa7a59fee6033372eade4c8b80540e6b3c6c0fd4606f6167fd9d355f6bf650990f5fb64b27b74fec2beb1e0d902c536e08d199dd47734f5b7843935 +e541479fa9d88a4bd4ca0f6b7f1f06c799a8c1e0eeb441ac7b8439f08fa1c6a947bdffa3e31c54ce678bb5ecffd40576fba60e365bcca77d479f9169ab9f2f1ccc8468d53bc7ef6f2a4b533e8c3c73e3ecccb686dcbacd448932f3f7fb68a2505aefea662f4b87f259afe07bca12cf37de5b0103b7df99c5bf85fcd72e2dbccd +da26fb8b8d311836ffd5db5064ab3390a02ec5d4f08b92f18dec3d14a458decf147268a65b5cf85cb809e30e6a69b032c3ed89cd6703d4a7523ffb5c7863762556ac0d23ef72d41b5fc3c701b603771d859712d20bf486808158b76e3c647f9919f2253093ee441d9be8f638c38ada2c7f5e28530141d8bb0fdf2192a023f0dd +e57694952565c980f833b0448ef58ec973a1a6e9f2b497bf0660785ec578ecf9d3011ac2b390639c74ba8ca9ebbef87963ee133babce7aef70d27715620572f857413e02701a1070b63868b483c6ebcf419d66e3aa41fba5c4b2130dbebb8977e20489a485ec415ebbac10b169c0b0f1d0c21a96c52c3a9cbc71ed008126b165 +d6247d1a9e2c712a162f0d894c7079d3a8beb96e25c62866b467164d1172e99b15b583ef5c26af9fccb964d15cc778e5bfeec1ed3e5713aa3c09ac3ab353ad6c57b91705f910e8e0efc2d68cf81d7d604853399f811ffedfc6da6030137b156dcddc026eea0af1f88602072a4c0f119717ad6fa98bcb6b78a31e89e6f2dff105 +c19831e7cffce0a3b135f9fe75c84df6accf140118c762dc8d7d24bbbd58d61b93f9e41d49581e7b27b1322d16cab0ad0f7ab9f6d4885a1d9532e9126e1a4a2c7316388549ad1084335054a19bd1cdf2e7828d529c2fe1fc4d764b31ac490f26047a1564aa419dd99c9144095e35650b47934f29fb49343702838630fd9b2605 +ca44e81c9b8fb87c9da389778ecc62b97f40d4f4a5a0a1c6644e7aafd454eec28d58e5674b03d4135c21feca01a3760d11bb919b63cb6721c94e99cb6b56de6e0a649589b88bb229847006ed55a18b344a4de9b175a2ba3739220e6c107662920307af9e1022c7335a0955ea358df0ada2ad3943680ba2e0852d6e3fc2264b25 +e0ebda026946f37448a80a6d6cddfebc8520f7336c499b18ae59daa4b9122f6591e19f47a8603b4efbda0c07b60f6aef095b4143394fe0055290ed3d3ee10bc224d0df624a735f08e1562efc8cc338d83cbe95d372898c0985e947d1d61a0fdad9ad8ced32ecc8de121e3c52033b6c606540e996241a981387f066209d8951e5 +c429ea9d461c269b00c787861ed26644469f9384884b028063f9b556d9ab8033f12e3df5e2ff8dc0206a2ed4060a9782b1124c7886227d49688bb433a3f7c050130aeae8c0cdde9b6d9cdb3b0d355382039241518bf44bc5320d0be029af33298ddfe35caa2cad8ca4c3938428561a33d67453d3d48d55964bc89ec16019d5d5 +fcc2f150a65b58649c0e7986e99703ba3ded0a3bf24c3a514388580a1c992b141fd974948aaed36ddfdbaacaa97890c55f5edb67cdd0e5c24a0af84d07602143fbdfcb751e6bc881b4899682c0b45ebb668ba9b86c8100fc1a4420190b51e832c42ef628be065efd668b93058d3c50f4187884f49714c99b9b81011165ea64fd +defd2f3aa104b433c09ac4e959331cc538e6857aac1b1d34abc204712d0a3e081eb43219f24c5ef2ac7991377497231fc35a504fd18bf370bff33f9518b1f6ebcf7ba527ddec3c27931a2534311b5af43b615369b2436f157ec9c4924a74388102d7d702efedce8a9e1513731c2eb4a498dd38ef3d48c2671bf0556febd61105 +d24cd7fc935c6649cc1c4016e9b434f99f8adcedcfb14c10d19460559b79604504ae630eba090a8e12dce804a98754dfe97d418f69faef31b36c9b84494a52799fd6f6554bbf00bf09c6ceb31d80c9e898585ba514db0da2d5406242ac6425da82f54b59116ad5a8667bbd36359ef615124aaf49eb013b12c8e73c95ef1754e5 +d8454d83397aeb2f95c2ef3f631b09aab13bee7e2ea80a60b23f9b62a16661ce173a4adfe7cd6add3574eaaa2e3fde776fae2512f896102efda5a5d7963b0cb0d24c64cad43df1a404fb45124985fd979ea12e3b8e04986dc79424aad069912399f9967819336ffddfcf59256d81c31287bc0f826259d7ae518f5b4c90bca635 +e5294125e579b5344bac95b5bacfad8a53033192761a264dbcb51cea719e9c25ca5777d647ae22590a3845bd525e431752718570492bb6a7aea199976a5d5f88389289ff8fa3353e1c28ca733bf509982a478433f0690a564ffb77abd035f3cb0e3c2dd9cfa27d0213bc9a564cd40421bd8553c08d9cdd2c798affe3ca61fa05 +d51665034d9ecae075b5608ca2418916d2ec4005282a8324e438aa40df2c4fbe78d339c45293945203845d3ddd9b5f1829c4b7c9f06932279015db0426e12074fe6922892d1accc23322bc6c559b6238ab81318e19f2b09f8743c84b95a873fe6e8aba102ece14ba72ec41336de899675a01bd97803e9dab17e8f5a039b5e8dd +f186114528ec964fa924e4d9ab4a8968543c4edce6b5804d36379c8598cf0cc805034bf8f9d56263998506670eea8719bb510d5ae42b56906cbbbdae35c1144052c937d208e88d4604ee9649efcc4a6bab36e5610f3dd430838947d2c45147098ca1700c642769e7259025793028677068e41afb9ce631593b93bd17ca7a8575 +f16e8d1a0917b9df4114fac8cf2793b05359e131206f02ac1ff291ba5528232ae94b0f6043184b6adc09628251e0ec605e68a9c1e1ed0f777496fc8903fafbc624206a7de1c8ad6faab3baa383e7baf8200d55a5af19ec1344410857f3c8ac8d2f3ead5c582215344e23dbeeb91c862fd3f2f2483eda9edc4f170175f6e95b1d +c338871392900e2f76e013b9f1da842dc7e7631f8198033fddd6e8eb8badf74b402e5deaf65d397e8adde339503a42b7c7e0f8ecff0e09dbc8d29c58650dd694004576ed197da2838536e3b4343d3ca0cd3b762ffb994343e546c9108cf4f143c602749daa477d46d2d55d3a34d666b0f4fb423c2d485615177fa71329bf22c5 +efb3426bb8d11a7e6fa89fe0cd868e1a58743794ddc94b08e8b187efd3a22080bdc0cde343724416e6a2200be91c1f020c2256aef6d66f661c87958c366574c0778aa225160bf2e5bb4297a3d9a22ebe313532b04fa2240f93568a07b1864632326bbf5bc9032709b9b219fe8ea3922fe113a2b772d9fb033a5163c9fd95c6f5 +ea0dd3767681178a62cf5d7503bcf8079b6498453c06fd462dcea80c475c400b6072887616efdd6892efc06503c78dbc0f1e77bfa67f34f3ccee38c1843423fc5f3be216fbf950a631d90191f9cd3a7fb8d0af712c48534a3651ac75b482e1e5f9b27c9125f08a30480d72a3a275b039c6cda359798d2e75fcab0dabc7378d55 +e7e12f4fac98c66c07c1e5bf04c07154e5f5718c51231e0d551a2995f207cb71ace535541d09024dfc68faa213cc95bc0a1b07bcf1101c623560a1f4ad1ec3722b746f202537267c69641d91500880b9ad9f44d07c991405a1d2fe51f5810d7ee081d4113e14654efdcdfcb5857830448b1af7dddac073c4728c798cf393b315 +ca0a6fc6aeb2c9b9d4298060834ddf9c91f3b4f5287a3dab4d6795955fcda75753baae06d7e73eff76a3a0372b90259d6f805e2a2c3aa6a368e08e8100acb1dc2aaa5126bc91ae8e3f15b8cd638fac50f772832b585b71adc258caa8c496ec6adea9b7b7a0ba2c39dd02df58d83ac79bb0dc80367c818948f056d655aff4bc8d +deb89fecf1aedaddcf0d635550756c7570c088063eb88c84fa9e4e5b5da2c7d2f76fea3959f771cdc05a8e1066267ad88dc348b69d6df1cdccd9538ed5049f63a58d8bfe3e28fbb0978f9aa1a300a4bbe9d07cb83f661b2d5fb2b3eb7be01eed03be859ea4eb5cce32d1fa3919966e1e56d388a9b860a446c4a5810aca2e2075 +fcf39e5ba24ad54f27b50091da98de1bd4799c605431a715acefd96fb8bf1513cfde5f593f22686274fcdaafdbbd97c08f02a8d76064fc3af31041459747de9ef4ca9ce18dc252dc8d61ada1350100ecb0b1f97d769ca8bf5b937d0383e930a4c24b68b7a1742472732efcf927b37245e82fd08162c25178d477b515d997b915 +d790dce9372e7ea1d2caee36f69e987301a1a7b1e52bb53f091a2961af1c324155d5f4be1df673285bef975c141e583cbc91f0404c2fd1d005b00ac8b08c5492b1ccea8976dfd1c571471b50c521869a85263989f9857ccfed16022ff08573c672fd29f37be59dd27e872ef5747f87b0a641bb6a8ab487adc41b900396090d35 +ef8f422500d4490352508c3e9435ae2bf8372edec93d6a850c0ecb2f23996556d3635b4e0bc13544481773cf1ddc2663c36c7b4d340a2fbb3356597eb60765f876f8c002ddedf606cb86756c16db077a5fff5646b117d21e83facfb7bad9366e2532051837a3ae753fe0d494c34835518a8c247ee2d2079259b72c2859b4e4d5 +d429f267e30c0de6826ce7f65f90ea8033c03ecfb5dd9b4b90ca47495ab5bb2752b681b4ffbacd304ecee282c922ccb042a41b462723275ac300c8b0ab2a8bd83c114b4f516497c662e8193a8bf54782998236538f41c0f5e6ab06182aec139a7494e1827c3b78d7bebcb07962bb8d1569ea0873c9c6318d3d1cc2c422b5a935 +eafe2313b84fb9e62849079d30c7574f14bc26f1b8fdd3f3d65a81508bc7b6a08a1d568c1f25246544b939f122b1cd0f87692f0c944774aaa8956cb3df84882f5c38eace27bf1c6a3eeb4ac172c9d226d540e7a77fa24cbe6c4c4c79e38d23038b0d517a17b4262905845c185ccadfc2f642752cd15bc48a4ece8eb5ef080d2d +f470db3a4ab8db8f77a9dee199005f9e8223821083326eb8e9aa412669d10c7e018159e8fd5ababa671c0c3403d7e381ad7786275a17e1ebf478c75f5e945b07d59eb7959e253731b3266dec6193a2240ecdefd82ba1af59829ad0b875724e87a9f166918bedfbfb60e6fbc871c4bf5dd0dba6c3c9d46b7d199569c85b14bb85 +c71f44dadd29e0dea2f5ff3fefc4dab4fe6fff677c82058da2321f5935b35fd5a241ba8760cff8c910732e798f5c1a2a7bfb728412daee252c20f764c818c7d4c07c971f4a60f0c1ba922dbccb419ae96c15e13bd99526a947783b68ac247532087709a6b5c42e58a6f1493b5f418d5d41b0163441cd6d4224cc762767b76c85 +c4c55b788b5ad843666d57cd124ce4c9d3b0ab6f4074fff4bf2f77a293f35693457309f86e6cd9a3351609672a5b7b839ebd4354d565a44911cdca108b95d5f4d51ee45bc5a2facc0579424264ed19365de0a50915fd5f4a8bd8b8cea7df39226f10838e84f39383927b5385c4317016f4946f7ba1c1aea81d86431caf3edb4d +f8b46dd21201e8903bbc4c4fe61b97aa663c3002aeb8434a6fee34b462a781fc99f0a483cbe9d0f20bffded09611706e81e8c2c7070a58c31acf85a65830d9b6b51bf0e807aca1d291143c26b35be69db73ed5589ef2be4d483f97197a5b8ec3131bdef4675ef7be3aee533ba6bc1330cc6d2fcef3dcaba336dfae59a4e1f725 +f03b62129d41a09715c112d11c49642309f96a429af38086aeadf7c9e8d53bf487f48ae8cda37ea64f62132467f4a0ad6cfcfc6ada4f89d4c48f76d5f3f283bfc2076bfc7f757d94bfb152bf06a336f98a4ea411af5aa068dbf8e6ca2ee9d80ba2e48fe71bbe6ae886d3bcdcdfbfd73653ce43f963435181a9cc79e0b27a02e5 +d861169853e13e90282dfdccb3d9aaeb10c2c4e996ba1893937e2aa701a56cc27ae905aa733759f008aa1c0b67f6f2a01ba176ea222b7d496a5769250c343c3e7fd0489b683684efb061f35caca1725e01ecbb4b4d3b1d83320007fc66cbf040ea0a6aca3c5f2a88f35941ba7724c72903597aa621e97d7e09c8965403e1bec5 +d9acf96b15169b736c44d9a12d7e987e2c2e2982f2aaf5a446cb063e5d911b1ae053ff07913ed7c521d30e1f07534d223c8bd2b50344396e839c9479adbd23c196f01c166d7eb2906154b3584ffd365bab7164c193a5691a5047563357026f2b404e17beba02b1b000d9e66562714413ac53d12c84969e89f014175ba27d8d25 +f7b94f600362ca592424d0476ec8b022bab10653d3602273e3ea8ac58325db414f451b82c9722803ef553d0de850c1b8c230accfc51d1b759113e2bdf16d16e8e97b46a25980015dfdb90918dc3f3bd36fbb68b7954edf991bce1c291a564259cf642531175b52be6036fb8e962dc3d8435ee4d0baedfe8451355b3164a3391d +ca0ee7ed1ca5bf9222afed87fcb7c71ce8e73addaab1d3d8b6a22f00d0f4ede8b3f3132210995fa34fd85548740079313837fe8eb8f2fddc65f9de71031af83eb1aa6d8bdccc63a07c932b8c0b1d447bb4278be412e68da81b32ab8c38a9c4d8183fd89c16be206ce26c464065166c3560c27b931fb321ac06322909b5f370c5 +c3e5321f70946d1e3165628be877b8b281555e2911dcd721546811d082ebf023c7a745a119a40047e10715b5214129e9d29fcb2d7743331fcd43b6583696e58cd388892131fce98ec1c5b470492b37ce923a74e27994b147e5ee42ae3827165a4e7e532d2b99d9d24c35af491b49665b89e4ca5039ff9d0e71288b4b8d40bfdd +d3c922f7e1e70137012dd1489d3c76e8d35a4a71a052ecd459a7df03403afd9a530e0b7f23bc500abda5a4ea51016626c17f410d85cc4ac798d1a264c94105b736b7b0c8b5503ec701ed80af25a8b2c341e861f8d0ff8f0a13fd01f0333a80e57e0ed6473edfb0a51b67cdbddcc6a28808b371add5151c5d5ce45426e0f6b5cd +fec3c5a877fccbedab3019571ca33cb89c2a2f15f7188b18e3833da5c015527f01e54d2306086ace38b05e2fd9e5bd8bd4a669850f86853719c05715e885ed8b07d01661e0ef7e59d16119542cf6fcf052d609b81b85a60ec50933251d83aacf39f268e4c3793a467674708068d24267678b550645229efdc6cd54acd7e2c14d +fc22a6303f3e5c1ff1c907aa267144458f742b0c2877cc0ac406826c4cc68fe13758b928f93b1dfb2af2f6d7f72ef79aee62f5976ff3fb36478f5958956463417e74e6d29dd438c1f5f6e20d22a046a662d442ddf89c45ca850ac31e4d90f1b4148ba7e9f509b95bb7b2127f50ee3ae67a637de417cbaad674d9c9ab960d654d +c5e7d7d8bd4eee2149c6c8f40ba59f1cc8231f7ba294e6d5b7b4b23ff8a824cb1d806eadb5d3be8444289b12a4e1622499db5bdc7ce400e60aa21c7c8180caa399b036e388e1b777a272174604714c5944fca044d6085665eefe70ee6bf2cc00c9b388f1cb96983bd3bef01474714c89b24cacbf91a8ffb05b1ae9c4a4188bc5 +d948d43d26093a2ba551b0b2e23666c807e127b9dfbc43544e28d7eb650f537f66f7c0818564c960ad708ec73b7b9bc5bb698f1923a76e16d1694e9ebb4c9bd7930a951feb570fc9ae1ee7c6da44661e3d42d6e61bffdc063db904c70c23f91d690f91a80678996dad357e934ac2d6a7d6465b2f2cb1b426a9f49c58da5527ed +c05c527b0d5fa435fbaeec6762d909b72b29a5a9e78d723e01a27d3e279a69900a4befaf68f5faa93e1ada0807c692090bef24ebb7fa5592e6a6f914d16e928dd319559947609b39857f31e72ddd5d60fee1b05dfe8ba63ca3964bca4f5a4b713ff38117be91e0c99662de63068322e462f6e5c26728edfd1f2ad6fc9b0e7bf5 +cf63801c085a59629b4cc9ff4a84e8c591b78637935e74f2a0153b168af7319393080d5628a9f42e3d7e2c3f81abdbdf0e47c0803144323e40b67fafaf580c44a56361e140d9f2d7dd5101e733b6225dee687cd695899d1f66d5bf6aea44942e31050de4d15c1e575f9704a5e583cd9ff6bc255a1775a5c1fe2929edafacf265 +ee662270fbeeff37339f9531cb3cf368439d8965d87eb53a6db314e5326bcc25a8380ff08900df663bd18d9f955d05743d7b3689120df9bff9e2cf5d2576e5f8755e30daaab8f923d9f7d623342469d84e649c47596d4b58d10022c0d2fe06f1dd27708dddd8d8375e32f8f147e293832ee60528eb4e7b3dd57a715f9327ab7d +ce0a029939cda5b28090cd0a778c5542a424f9c82daa501da76aff59fa8ba8ec2c160dcf87e0a553b29ee948c66da6979d1ecbc7b37c91a89860bcbc543d46587c5b1b992e490d9c2d813d0983e93e396baa79dd4105960c30a75ad3492b873320cb4b3442e8d6355491388b6d59153b4da754a6725065607e6126dfdca4e27d +f2e87e164154e1daf3e72ec783ca1f8a5df3354fcb358ae29a4166ef64129764ad1aa1f33fcd59a3c86bcaab4b5f5b38395761539995a03fbeb491ed66cfaa68876b8ec1a0a079a127e14f82d866eee6f9a8077700c74855044f95a4ba1ee7c2021c7a642c1a2e734f8dd6af7fa47445b8c68a7730976be70388b737b7e374dd +f968866268344e69654fc79a607be13aa6407de096255f75476165aa384c674f795fa02a5ef8f19304025015baef3a7ee33513dc9912d512df4f7beb23140c011ed78a0acd2338c6ccd0eb1db4c1d33d165a168755c55f58462ba874d436d96e841eb0a5d00a5fa230db14462f03749a45a38ecaa9c26caee2965b3b2927aebd +e783c7deda612f1caefa7b7064bf23a0da09d2ae09d76d3841171b772dc8968ba9e53ad2c2887999268cf9760afb9233f8b9037063106453adaa744ffe6fb9509cdae6a1bc15f4f771b6c602d68d1a28be70c8a160d58b9d289d8347c5616936b63c866e7e5d6bf2b0143e0b6581ed2e2b0e564c7ae20425fcb18e4ae46665a5 +d978ffc21493198cfe598fc3d21d66b33b9c2bf692ebad110425c5f048b983aca82ddd6205a96ea5a61f42e1aa062c7b274635dd3d464cc49b63c7b35639af93ffa1c1e0b05b7303abdc7f25934decfebaed85a9575dd1369097211306bd9cf032a6cfdaebac6cd109945b7d56f32a1af5b9863e5a6c69300f87144b92db6b7d +d0422b9bdd9a3edb95cb89d1eea6ca34b686a61a8b6512c8b1fe1189a3cceb7734b8482ae1a4e2e00f2c567dccdf1a6079ba89d0116aa2a70c8b53f0aee2b00fc9e93953bbaa090b4e4ed7a8c41523cb7b05d44c267994e1928eba2422f4befd5ebb730b7e544b0d51ba4052b8724bf16769114a43dcb11b02feea881d99f705 +ebe131fa523c7ed17578e2e5823da2c3b192ac207dcd0d146ebcf735dbd17459132dc47b3d16950c395660957819d219b0d1e0cca9dc6ccda229a51247ab1112cbdcbfcdaff75027336d38fba3ca3b74b9a8cee891ebf6d08b96f33d77730149e52e8c8a38900cbd971cc54987a44ce90986f2e379eb9c6a20969694ae39905d +c3a99a38a01217be7d6eeb8e2eaa56c4a43feac43006201a3f8328de13398f452e051702cce7292789b98f122ec96cb82f38fee6ff5d92bfb420e8ed260ccace5eea797479a20fb214fd20449b012c178480a35d4320046ad3889f1ae3807a33ce322ebc5f6ab1bc8f39e05231db9e61d97f5aea8bc892d32bb98ef692689eed +f5ad28c75028eca2c03a38cdfac27e29cb9c4b99a15571a6c1342a9ed21e954f5c1f442b38324ac13951f883991d85c7fd03821a45dccfbf076c03114638af9fa416a2c71b12973019d164b04993ad85102a9db85f8bee7b86df5ad9fdd9cb89dfb07e60d48c0ffdf68a86760c77dc670a1b3b62d7886e4ea96f2ff88878db9d +cff89d75be266af6afb6b9eea3e0a02825b85cacb908c9a4911be00f3b7c8c5370c05c0846979c7fa9b2b18fcea91703b6be71912927d49250ad9237a59968409ca55cbc08ebc54ef98338c59b8600ff687733e4178dd09b49bc9570c86e405a1f137a888c902d729cbab861ed5f965e1e50c9d8c3b426d62185900429a22125 +eb8936415ee74a4aeb09de49a0d7f4fbdf19223bacacfbaedb5c51c0c8d52f6c061c606060d3f9119a4fdb7b4f17b4a37b624392377ab9f81d769eb45495272fe4bbab7d110c3fe7d86d1ac2d91753df8915a8161d2f7db2378e886dfc0fe44f49c4fa6bd601c572423a37f5f9c3f40ee5c04c8f6cb5452273c0d2cb4040f0dd +f0ce9f411618305ba4a296cd22040713246e2f3ea1f723d2e2643b9fc536a864458b48e388e542c360bf5917aaabfb2cf1ce1e30d275ffee99137dd3317e19a2c534a6113e3d89a6665e55902ac1f119a0ebd3390ede13cd04792c89d349fee7c66f17b30e38f78811c81d907e275167b5c3000e58be7bda033c500a172d889d +f7745942de25894a96defe06117eee661fe59b466be237cc66b0e3854cb6d3b9e97c8b52e216458c6e9bfe9303299ac6294f2d62000a964b128742616a726c89505f1d2f493a68e0ec22b80a03e0e478bddd2760b474072490ef5dffa6fc01410a7fd4cbde2aa34b82172deac580cf3c3ee442a3a096597dcd8eeefc285b0a4d +d32dd28cc369ac18b8a088e49062c13f8a344b83cf354849104450a743b6adcd27ae6c321068f49a0334aef4896d83df1997b865f658486421ad0c5773652236a07a8846ec484eea7830c30884e7c20c35d035bbb4b47a112cfda9ba84db9b3dbb0b0edde6d21f8022f5979b3dc441245e38bc47b817ee3b0be50668f8b45cb5 +e26f4e6b3f3588b49a92dbdcdb3e5056703af21b7cfb521d62eb318105df1d3ffa8001629da6f5d3429dd767016ab9707c009a4de738b74401ad916a4f364b10abaa44471b1f16fca5c12a5bb31b6a54f1431411b2cba27d762a6914a6d13b52c42b3ef5838cbd502852bd490fc9951505f81e8dd6f10270861d091103ad5b85 +d640540fe9113341d00aecb996e15420c007a758094e6e89d05f80c2fdc165bd75475286235f5bdd659227c55bb901c2e55724c2af4baf1b989c71cd46393badb590c58b682f0e7f0ba01359232b21aa0fd2cf1a8c71be06739f1237a7de275ab4fe4463db572bffb219e2fd11b8aae37d00227dd0ed1fc060f1613d98d8821d +dc86ed947a567f74bf4f8c8b2f32654a0efff484437a049772e6817787ddbdbea1ccf4d61ae351773d92078f2e2fc788b6556da2955203b2184b0947449ff778359a1fb91bed9aa62885e5c4e3f7a359185a6816c95be610ff82a3d528cbad1775884501b5d5d200303249deb845a6656d066182cc893f0bab7523a5f8ab8185 +eea1ee526723cd0ecc2e218d71fe39bf912afc094eaf6acb4b3f2d76dc104cc9dc5a59cdf7a18493bd90462eecf9223ebadd096d07ea4f03d680f38afe3d60eb8059ec466294580ea9ccb81dcbe840fbf7b65b8d15d624db9b0b9c84d0c3d2549e3782fb1bef4e264bb3bc2d1664c140b4854f513befc9d48b9c13603539220d +e12113c4525a41a9f3858d26a91cd61061bf7976f1090934eaad962220e176519cd512c103cd4b655c87f83dbcb274be6dfcae131b017ea0c3df253bd048b8498256d2cc862eeb4a2d957d9f3c6c746bb2e7192c116efaf3e3ffde5af9d129ee65f3ae419c6130693113f3be41af7f8e2d6832aeafd2647544eee1e6b8aa816d +dbb906ff42edf909c7c1f3c280b607743049440761021499a03a9992de6ec17bc32458e7d697dba61f747d0ce110a24c710d00c597316f2d079f1ec9de3f6225f3940286f4be61fce0ee9649460ad389423a25922015482a6da7524bc65280bb0de9f72c81e459d40706d318632d4a151be0f8ae78239de005ff723ad4d88abd +c82ae8d45b214bd7cde7be84140b8f80b3180966c4b154c6c7d77b94667a3e710ea5dce00ed4a048a8f541cd61d487d014d97c0da902c2ae55a8e8eaca23bb23dea15c68a9ef146d08de1cf61dc589203acaa42df4e1941cbad3d338b56091117af1c9a23904c3b4b7832482310c0e2f9261b1b703e3324418972d903881e135 +c0a6505471e66553d80b32defa0199a97d57b22af9ce17e2a2bb7eaa9a0ff64e76a5d4e05a982d51b8db2e9941480de002035f3ef566f10bbc035b799c03ec58735e4f590cbdb14388b84328d33c53844b757b4ca44dc3caffd9ba8fd6b134da06e0d4c9d8abc88ed43808d73d2b2fd4c6d4e8cd70f91b22ee246e2ffcecc24d +c130266cd4a28926e2f8764efdc55e59cf3e936c5b244226b728702fdf5be181ef9cdf6b4d2fc7046236a741b7b9f7a7c61133493bf6b8578f990a80e68d693fd07e7330644b13a35bb2041d7a0d83a0ff31ce8382d5e197f22826930e7d1eb99bc2ac85f7036c54288e64bbaf60b2460a58c962b028a4b9dba26a9ea65eb54d +ec0a61da39b2b0af9b1b04600ca6f3cfd5d1ccae3970f641999e2e04081c2edac59ad2e25c9bc2babea9afdaefa585b9bb094945d75c2651085e67350c7f89cbd1618129ffcd7f683a10d2de3bc76420436f23967b29d00c613e61b1950384d361170a8d6207c05fa6015cfbc78d4de334a372a94c2372232d56a14c095078ed +d95927e4875a12ab9e97d72bbe3792ffc100a88abb4e665b3de5531f6da18497956af30795a8219bf05ccab568333528059c85e1e1c43b28811838fbb19e4568a7adcbe7111a260562b7feb89e9d4e8c23cc4781004558618caf3984cf6b1c0f0fd56533713b855781ab6520e30e1c33ad34cd9fb192d302acc396f65ebfd82d +c32682f151c85652f533554a95f4c5d1063c73750be7adc84cf437ab072fb835f42fa4cfb3777827d22ef6ad96d3e5ad2a3022464a9be7d861b0f0815e3f678274bf86c4ec012082b46a49500f92cb7db69df1da1b9fcd2cf4d47ea28d6581adefa9aac478781f74ac7afa1ba709baada5d0a55b84446b849331bd450b9143ad +d639fb704008e9fe6288f4a3c1b4e4bb255af723f5a222eb6b17dc79eeb882254c07634d9995fbb231e6713863185102194217c72011a1aef91db92d101dbd0780f23f8e281156168552e2fd7910c5d31510ae75564709c6ef3ca59f0af02de59d7b6b413b31fb17c41d9e948ccd2d4b1d0ea9ed58cd2cb64eea4ccdfa7a591d +ff34ca1e9b2c4807f0c9d8940074d34ec8902310de7223053d8902f3643800ac916eb801f405c6ca4c3596e3545d0f6b388c4229b19638b82057826bdbeb1c65c7557848ad894b30e336eb812a0ffbf33fbeb42ed744c7199092d45178444093d12343937e3c356bc1eed086a2a6097bc99b1a18e5d3386e6410d451c52ac9d5 +fa3cedabf5c5865d9609117f32c1195a35f1f66144f2d8b7bc45f93d03718b9feb4b3bb324420c274de0adf7538d003d689de88f19e710e0774f6c9d02bf1e3f4ac646c1f2b05a2144f5bfb6bff0b9421338e01911c39d58593a13d5f09488dc0517cc49a94cf0e7efe39069ae5ea553efad66f049a3a1484bd6551a98a2c4e5 +c2a7edbcaa3c4b5539b1667c612d3aa11696347b7574722a520a30336d75f3b51cfa7bd8bcc94f393beb73cc34c27d7619d851f2b8c1738c87e7d502c2dc1fd979e66ee2561a92281f4d100a4a3a89179cde82303385dcc4c9f048f399237545c526e03093a55fd90a35b433ff86c635041674490876a0ee3407e13a4432473d +f46990850241249bbb7cdd181bf26bfed6858152d0a2f3a07e06015d3741fe9079527c881465364f8dbdc7ea9df72e3bc86b1534a1b9a9e9b0db457753cf70ea06a3440bd1459554e247bbe119e789d2e0f4857aff24f410a7194521dac382a81ce89040601cda6ceb76b96267ea9063777b79b32712e1a1ff0f8683745a576d +d585d7c8a09e54f83b1a6ea405693006b875d985f01d9ada9d414a44aed16c2759609706a07e926d3c903ff6abaebbe89855f4784de2975096f249508d4a3c2fe55f85a5d662ee4a48b2c21e9f862056bc6b0a802c5130ac4fedfef94bf402988fa29bf125cb933ae316dfd157bb1b4d56c12ac3f5ce2839e41cd8a80c3db045 +c97bc84c797cc5ce3c2be229b0746bc45fab70f26b59cdd7cf7a5a0b96cb7c1e5e6b97d743ff7acbe33f2356a65f5a885d7b0364474e2c72bc9d21099282bec72d23ff3812c2dab68b7687c8b4a49fc6c714dc935f69cb3444430ae719cb3aa72bc80eefddabded582da7098252dd8baf3b60bc3133c2dcb99e58b6ce9ae77cd +ea3d3fbacc5209a15b9321aea7b43727165f34b45fdc06169ca953dedb9b56a3f71fcee48217c54cabb6813e36957ca4453019496ffd665109ce2096dbb6ef049d7c47bcbf461532069522caf053940df12971d7748caed05e9e654110fb97d199683769a8e2dc7666b9bed501c9bc93b901334fccde993d646f9f2ebaa0b435 +ea860c75b83bb20a0ac27531c1ebfadba1157f1f398d39c0c1bb0d6ab81ef2889a400e89341712ae52a4e0e155dd2e2db41863330b02bf198793872565b515217b33909de3f12de190cddcab5ba8ee70af4f29c86273a62efe6fff135da9240667bf7d2ad03ec6bfa0e691967d56da9bd3c57ff9790ca2e905c86fa142623cbd +eaf4e7490624a6174c26558a38fb59f80fd1e0998f4a6ab68a2b02ff3a81905576bc378f6d3d413926888c4067959b51f0725f854fbad6213f7c99416cc413dfd90c814e11fdcf373864a2197bbffa3d1f30a79f008b90bb566a001943d37dc135eef8d10448218e72e2d95a7c02ae89690a6b1970b7f6edbd47419888210aad +c67db1f755259c3e502629df50c9e52e55a8911d237797c614b8e63908e5d3c1f13f9fbef4a6f54b56d8db3ee99076bb01a34225c31e316ab65de36beb85753384bc8979f19e54692d4312e0a1ea82abfdf1fde7963f05d811600311866becc994a1909603d29d90c6bee021b06952c1ea7b99d3ac4bfd0023bc88698651b415 +ed2741be0f80765dcbaaa2df41b383b3a66241743a2828fb34e2f941ff6c507d6a7dbe8706cb1208afba66b83df09562a52579c54d8889220d44856c78242722f1510181313931892ef29dad2c3e5938ce3e74ea24d3ab3a7275eac299809242a1ac425984e0520f03991342fba98292e672d99a9d0c063904428badb005eb95 +ca82695fa012d5e6f3c664b23458fd5daf95c94b4299f936f8b70b6bd79ef243b1b9f16443f5f8945fb01eb0beee10c2bcca4225e6076ae9f5576168826442d4318afcbe7be329b30f0b4becc72832534fafb6440998f31acb31258673a9d202d5dc5e1ac0e5f282c61eada2b6f592aa2393db06bcfcb8166f3439e01e62545d +fda28487fb44a1cc26896298f6ef49d609087687498713f41befbbf6ce0ddee25a61acce094feb2d291225b00be8fd93ee8ddfce42999f29b149cb00f36eaaa3d379e9802f70c3ff58858acab0ed34bb9050ceb4281832a194831bf82d860e27474fdbb2f7b66eca12307dc685bdaf34b623eafabe885c269bcd6c21a52ef0a5 +d8447ae18777fa8b39f9077b58e056dc22330314c427e6e8d13fc6a3610a2c1286246a724fb99312e7f915d891101b1d8815582165178d55bd4a2c367c24e78ab21b13a8b9cf3ff67541fe69049948b567fb2f2f2bd4e7c711693dc0b6ed7ead617a447fb5b5bc567f53be6bb0fa097eab097d91077fd6ed1f02db4ac96575bd +ef71e9bee6899aabd27cf4ab2db213d0ee49214291d073415a2663e370d01fa741495af57193e52c58ea3883b2a13b79d85c68ed4032a0fe96e5c005df2b0982892f1d6c1c425c04063122ce306bd1a194b7b5a1366c2ec673608c56299f521f0c3493e13bed9df1c462e36f3a0854ce33926d532a3c05af710f5fdb65d36cfd +e5359c379e60c97dcdafe5a336aaa0db6659cb8f806a5195c7e13211b396324e831aacbb8691146ec906a96ba3f66cca0c8ebbdab0af793b24c94081dbc19d8f1ee6625ddd16103bf49804fe41525c839f47270d6f1bb9930f46063b593d48145c1982a40cc78421277ffa9b134997b0a02c8af22a51d0c83333433b3fc36ba5 +ee72e1aa567796208b5d8e66fedd555d5755d9c353a5d1979d0bafe0201ada1abf26e9993e2d1a20fa6a1f374d8ef39f007120099df38d6481584e6c6df24d31637d5a4d5c92f8496335ae00a3c7a5b713ee5ff12d9c4dc48e02c8aff9a308592c8ed3f66c1128481059266c4f852540a1393139f7a2d8d4ba6548505af6f135 +d95444b1c9c3d55cfd17e0348d0b59c73d95a44621d30d3a6fca261a8a1a4fcca9c3e4c218b1a88c2aade830d7e74b558ca56446147f2546fc50a485fdfedc0e258378ec1364edcd99a44d4c203b01db566b20528da4c3ccf4593d388fdd27d6400fc1e05bcafb03ba5b59d0cf3edfc66c172a6d79d50c9f6efd6e32bea5b0e5 +f409c82f06463b8a59238727edc9d64a88747a76a4d8a0950533a63664e5c231c842557172601b388f3566d8ced5ee0dcd34eac2d1fa16ba38efa15734ee29e4e42ac00fcca1a9919b4c7965c7be46a72bb6a3a4539fc9fa9f9fd3732786ade282a4fd6b724c2638406b97e501abc5fef1f34b9a4117b54cbb96212cfb0d154d +ee71503445dc30a2e629e73523d04c9afb0ac5bf8c93dbdb701b6d4f95faf5f619aeca3cf32aa52d7c441043c6d08a46f20ccce3b63348fef6e5c5ea655a25364ec3cb997c57d88448b1eae83f65694df426c4b265680b8035a589f309bd1bdcd77615cf00fd98f989b2c1f68253959ff6e45e53524b49a6b81500c3b1975e7d +e8e6b89a01bb23b1a1e501e5cd91b8d7fd676e3b4c82045b2f3403893a44cd2beb9d1e72ea33ac197edf6838c445aa924e0174a83ef14074be38957f494f7aaef38cbd6b42ba7a3a3e4e570f0c9fbd0b9f0876c12f66dc7f2806341851997d623afdf8e1885126cf64e6614119fd9096da72f8bc765e87cdde95f796e03ac72d +f28d5a3c2f7f68fb0fa309881612184933794a4e09b4a914d0e15962ba28ecdd3cf032cc79ea4edc98f96a4d97f9ebe1a42f858c58db0a88470be1d9a2f15a3899bf7ad8703425ab51d37fc186578bdb269fbe91c83c99a5763e433ae85a91559c177303a06655300952f27438adf16897504c23e334f2ad0b9574714ff525ad +c36710875e7737d6163161d4aa394fc388baccf567a9a694cfff56728f5a5a84818d4b4c2a2c393cc92c4e59972980407221c75bd06253d1ea5883729fca08ed04a37e2fdae1d71c00ce3e5ec49ea516e9a43ed50dc130f9ca82a93c735fcd9b953893ad2a2f5dce201db07f5625fd0c09cf357201cdd071cdb442b99ee65635 +d9fd2b8e5a92de3cac06a9b941af8f6734c834fc586ff9664332272e5df184d6b0dadba9e1d75800bfe679a15caf98a340b68f1ad405cb1b92e1a0b5f176419e72874d162ce0966a24c15bd67d416230b07daec55774e40e130f5b59ad8cd81880a15659e30eb7c23caa315c4ddb1f7fb4d622b9b07900b28cb2c8850a9ead5d +f858fff7b6d38f73bbc3118f1a67e787abc04c759a97dd8ab894f1f4c70cb7deedc9d31f642af1deee7043deb93ffbb3cb2a50cfbc7de9345a66023578f8e168687fb8e54374cbc48b89d41a0c8ddd5136f2ba3119baf0b101d462db1f7d6adcef017b6ce12af71b3d83d36871836309ece1d2dd2316a7a4165a0c4936e4cf2d +c411fd6fae60edfaf2f378edc643faf350c7d5bd8d3b14ccc5024fdb2a720db6da39aeeecd2ce1a2abf2a1b2bde9a9e734f641bc1fbaaecf4eb979cdef4c72a3b735073ec40790e1b264519c00fcab517bb14e65721d5ed8e49af4d45cc272f98f9ddab8126322b0ed77f9dc830b33d3c0d00f7c989f000f5614f7e2b7a2dc7d +cdee1cecd0e500ef3a24913c8b4411fa864510348618d0497a27fd62406aa5e19256fc8c28a5865e2efb24b6e56280ace90eef672e2aa250355e6272218b50ff24351f52a9b6a95f3fd302da49668760566314b82dc4bd36641e52c42622fa3b99b0b6e080d2a6873c666757a809beb30675014a61ad558e02dbbbc931329735 +e43ab2cb35c4b7db2c89d68b1caa3a51e5939c423a7cb3cf907c66cb0dc17799a936cfdc74f6a1aa1ea1ea7b74640105152803015ca083207807ce0b3d27310689a15a59f5124bad92b0d7d38d81868bd8415f2ce12fd6dbdfae8345e30e2d3e481f736a9bbb0348c09c521b5a95595485e5db301d6398ff04a9dab2e17097a5 +d4b4aa3594a5a3764e984f8cf743bb10a9054ae512660fa39595e09b3fdaafdcfb25d814bf548fe219a3c932b85d1d05b6bac54704ae7b8d01b126c09463678752596df2eb1a49681d4eac1620718ffa9e1b383639f6a7e02bae1858ba20782b65b34a2873a7b20790699b219f6814edad9a70e02aa2241944fb548edcd38f7d +c424e212cd8caf79d592aba373255d335bb58dd95175bad04c404df2f49004585dec10629ffb6cdeed6a2b96f00e6a943533c05571b10d0f977f5ad6a63fdfd57822e7c9d83f7579cfcabdc57646dadeeb7e774272a2fa5739f9ee189d05ec7cae84894550e6b62cbf5364a9f125f23e6c66c4d6387179a480c8ca19a61a3405 +e35bbcf0eb6f2fd2bb055cddb9a62008bd11c72fe35f75f2f8dbd4e89549cea92ec4b559721751a938c7b491a79e7341db2f3fc9ad7210199fc1fb43a2d79964a7526a1f22130cb232fd006e85efbc33b03a437fc0806431a5e55fc3b738457c0fb9c2ed38d5a87382573b10202404e71dda4ee520568e860a2c5daeabf94d2d +fad58a9cbffe7e58a6fa508dc7fa68c3d16afb32593c74b324a96953e0a28c855cbd40e0c4edb5b255c3ccd3ebd5dfa03ff16afd76cf910cb513ffcb5103b87728d1d9f46c81f2eb908b4c35321aa78df4b4aebf26aa8ba4d7d9f4dc7817c5835a5cc1f06472467ac0c329952312829ce154002908a0312397d2001b22e55a05 +c2cc23eec815b9500fab0310f2ad42967b1d3a8a2ae30dc2e0b0756f03d15b986dbd05ae733b374e687d4c1ac71afb3062a0802016cf2f861c138aaa082570be4e3b9a3d8c3ea9d432375ce5672eda0d9264588ad178679401b1fbe8d39cc6d60866e7f7e672d96683069ffffae0fd0f7194b73552c2d37144d8da4d8f0347f5 +df830ac28a2ceb86de86c778400c44620fbca0ea597f2be44d138bab5d40d89ce5d802ae39b07186ce350ba22c05fb614af5d5e3e8796623ba2838bf8162bbfb336e159f9236ec9a8164cf7c59a59fe1309a4d740b337a4a0cc9d8a9d961d10e2a3e5d2f077a33edfe89721c2c53b238e6518d2d2104f2ccf3351bf73452ba0d +c390d2db5531bf90d8a92cbd10a58a255ef6c8d726b7c805479a5fcb0a805458da99c0bc9123aec25e4bbb483d7d2ab14c879ef3a278fe446c03235fdbc83c85779297db3ff92ddcc4c5a6f6372e924a5d24f87528f87f1f6cb0515bec718ec8b2f1dd334a48d94ba766664ef0c68b9d4edc80ab067e0b642555cee16492c735 +e9b097742abf5024caac24091c2c5890cb2ae2fd5e9c7128c961d19f4c5fad4bd763dcbf3c646ea3a1ac8d816b673af4d6a488f2e40ec0100d154d6d3fbaffc444bc000255950ed13da1308bdb6a8ee6bb3468564637c3c205f77a3ad718d7e90f02c19b6e497781d8da8053733492dad5d55db1b7b84ce21260c85dfcf4dabd +c29aeecd959ee5a8028ab5bf1543d47d90947a355022ba3c72db9304a49d740e7a3174ed0f56818a8bdbe015f7e11d05ea8b03a8e43688dbe0d7816fe9cc7311c546ae34c29d304fd5442fe8586a89b78994d121b0be22077879c6781c44f124a3be54e2a6fd72482777d63033536b8c01cb069c7ae01f16f3fc76e568cc5bd5 +dcba67abd50cec07b4e84ff593f1e89b7e30b28d1ba0704549a0f1de7e23327a8c997079464f0cc81feb46e3465ec010632b9ad0a085a1bd2a5e8c9778934241c68bfca1c96e476548296c787372e8057e7ecdab83493619ad567019616a53c19818228420509728032d8618a8f35fbd901fbd0a4c294a2ead8968ead89a1195 +d33920afbba20c8705f47c812f31cb7b36c7dd5e88a00ac9f555a6f0dac9026c4dae15c219f2760ccd38c0127671e5a33f00211c81343835d8cd856c93e9ff3a6c5c61730a7f388f6aef7779350ba98316f795b684c60950a18e52855288d2e7575315a5ed17bd837e176b0020e0d47d9c50b668a73c893289d66f79bcaffc5d +dd4b3b5cd4c6c13f1d180f2ced8fd94eed6df632f0d6dc0c3a86b08c971daacdb45d96b2df9f1911c8486a06fdd64535d3fe012c63bfeaee833e2b6a220e00860fe9360c9813f59c13dc1037addc9eda6784ab2435400342364fdbce37baeffbf4fc4d876eb10d8f48a6604d85609eb172939f57584087ca911511371ebb43ed +f445fcfdaafc3b04890f3a9d893bf0bab976d6ea0ab6353496c92a35812577970a6f0169a2f2c88dda1f8db9e5a48f1c8bd97dd8dc271321d4a512fbc1c22f8d433ff4acf7f07913fe3dd61c8085f7fbf5e38c73cc0bcf7143c7e7940bf891e48bb25e85e4ed044e299fc73f8ae5cbb928c350db712af57e3206075c31d64dad +ddd27681c4e17b61668296dc2ec218f7745b0d22d93b15bfd665cd1e2341685d1e46c2aa7272a81d2d6e492c879ae6d5bcfd27bfec68f86991448db24eb323b1a9972403d716074cc0e6381f58a9c414b67b53b86a0d4e572d117e31e831eed78674b63d8b53d8aa689eb629cff0625c90053e7de0554f26325affcff31c2295 +ed16929a515a5c173259fc7119e5b958b26a2c061788e7602bdb74df932bf38eb4f2864e8165d404f4bff6d1f4a4fbd0706b840a71f36fbb97d7da21ea11cd258914e1ad3c91f480154e2dd0588a52d30bad58a63da178c980dac5dcfbf07930cb7032bc86d352634c042978640ba314ea678097b6ff6bcaa5e76b5714dc6c35 +f63fb957a8f763458e2591089ba4c4c729bf996617e81bcd911297228a6b2f4d5f60f605ff5efee7513ceadfbfd2e6548850689fe19dcd9967787beabf44400c90d6ffa22874f1d14fc0132f56be66b3d0c62acec6243fc34a876f16b8f5f71564a58bcdffe30455573ca1aefd3b9fc62886d1ab6456de66100bac61d0c24e35 +fcc63fa090af719d1a27b6345425d6685aec8b3b8dbddc2b02e274205c91c965aa6e17761e448480316712a25b33a36ac31d959492c29268ea3f10547f28e90cc8cb597016f1f126148398e21920ca71960481fce3f6ee4c8064bdba7ef49af62cf3cff00cc9ad475ab84619244475378066ef891155390ca589b75efa544385 +e6da32d30e3934c02eb23cb417025a288538ccce07e0d9bce4de00d9492720692be2a48e82ac9a25144c0ec9b4b141c77648c35884316b311080fb0796cab75cfb93933a2aa9310423a7b2ef94860783aa27f3061246a8c6bb2e2b40b69fac7fb617d24d1cd094abb1a1c7bb46460598e66bb539aa0fa499ebadf05e151716d5 +f7379a553d60d6e51a8f37f2d910d9dd5256655efdc891a708c6cb52033ea4689732905a5bb46cc4d3183e74708578e39b0c8630cc0fdf50e5355aafdf07b478f19584951c251fa72b93be1a3a3b744fc3c911b0847b089eae9dd73ff7050a0fefd4508f549c37790de86c87ef74bb53a29800fc64fbe7d365e7e708dea86a45 +e933766d7ca9db5dc66ff2ddc7f44542daf51232fb9ab413b1d31aaac02dbca7113c4ca874ebf599363ca99572e19a3fa57761cdcc600ac6d2f0e8dd8471ee7fd1b5584f201a480ded805af6dbf651f1bf1f06757a7fafec243c30ef5eb3d94958ad6d6b9687689cd578e383c3f6d8e3fc8981e83a91dbfdb2c4e5292382a42d +ed0f5d59bd470fe130096bd507795c809d261787ce73c77de9480d57985759d4909b19b5e248bfe6c75655beffff3a28aae22c017ca4c20ca4dbc81288542fc5b6acb4df489e7a3895749d21f2a13c0cc7fef538dc806576dbf816b529c3800eed0dca00e3e4cc022dc498f8095c7f62573daa7c00b6a049fe550b1d74a78e05 +d647d958769ca155b5f98f62fb98b28c48bc228f641b7f0a05ffa07c35a7413e29f35fb675b6e2580bc1e37f95eab82aa64317fdaa73924ce0970434002d70d1a08f84099d1b151df7623ca07bfe27b964f2ef9796d1e48c172c812e7c039484c16df56e289fa32f8069ea99a209b23efc383bade2006ae60521ef2f9e7c28b5 +c49923c0119e93216c2684dbbd9385f2c59c9a7e6330a44479a62da7db661ed2a36bc6c22fe5a0d21fca0f8e0f2d91a4c2a5c42f917f8e8b8d3e33ce97342b2081bc65eee931d2659c5d7b997e74bbd005b91d1353a3a4d05bfe6d680053092f27832c22776178bfacae0117fe6d80f42d5385c38e0fdef4e3b8cb33016c4fa5 +e588adcbdf57002875cc72def03f19e6c24059914c3a6055a7d67374a8ba3d339ffa72067c69343ad5dd11001e98054928cc39ce04b543ea737b1735682cb87a9b82963080a79946dbbaffac5271f905b7236c6215a456cdc3c5c40fb8bad8ce33e67c1113cbd9f889fc785888f50bbec30982fbcadbde934d72d44990b12f9d +d36621350931f1a0d1a8b1d215c643ba1b6f63a879789a7f5ee272f570e5cf0ec06c0700fa2521e5d5799e091967c54622534dde1c7dce3fe659569320fcbca304499286c3798bae6b70f64b72e18cb4ef5884de2103661257f61d327667f7f63ef4d63482e9ade53bfaf117662b65e46db4a2f3eb0829ee5363f53d2ff38695 +ce5e2ed6e0b9a7e749f48d031452c9c49b9760dd74e9cbb87a3e6d208cdc657dae6caa5218e0a1e6118080e8d2201d541e9cfd9fd518ea91c88cf18d27e484f0ffd0d9d04713e6a6325df1912ed4ef490d1f8223b884277a45c18d9157f8224f9bcb68d4458b79cf80911990b08b40962f84b5481d08b834ec2b80b81d93f2d5 +dd6ab77fdad44b520ad7796facc9fc57e52a6adf9dd7acaf00834418d871f64ac6f2a44d44578bf2e4c488b57becb233fc3d1367b21227c42f0cfe256ddac7712556a3b15c9185f60093d07835e98d03c82a3b9ad8ed338a2b54d49a6d5320454be8aa1c444cefca7eeff7c3854bef4685e8e56a7a537b7a3cd088953301652d +def3aadd465c6faffd363a9c360eacce5859476883c48dde4c8faac7dfd58a1ebfe2316b8771dd04553b78a11d2b4617797fddb541f1e440b61ac9d1097fe55f27be65736448206d0349d2cd95d8856bcc9b770472e8e8db83d3ef3b38b40b83ffb2eea7c0f7596911fa79b320e2add7c8336a71dc1a75e29d415ba0bcf24815 +ec4150c5b4a666343f6b2a831f368a7e0d0cc7f64473e521f539eece8ce65065fc2b9c1484dc692c7f4999e766e277f6fc6d1beed8c64d807cdc10a4ca6db1ac8141406768fb4cbac076d77d9d0555d95fb60f878996a366b099e59952657823304a566b4009952c37389482e8979a5cb0a6f1cec39bc88442c91bd918187ef5 +d1b84e4ff948e7748adbfab581176b13a98fdfaa6097ca3615da0fddfe3a13134cb4737e440dd211976f4753cb17359ed5a4803e8fe8f485103fb47b82c923e4a923df949aa1339ec6e925bedf405f05863e2628bf3a2f4a301426e97e52cd17d5de113ed7b73425b5187a2442b141a15804a0e782528dbc545dcf90c1bb5edd +ed83c3fe0acb80253190a59051d684d71c8eb39fdc8e8beb75e0a166438e206f9e060dd6de4aa9569a16fd43a6337e34f8b038f19906c4670410262fb3cbcfdda0447e4cbb1c69bc2ede8fe60b0bf40ede40e6e380a5a16e8633adb18f3dc12b2e7c74e6dc088a23cef51792587dcb1ae90474f82f37c653d2abfdbf6350229d +f25cb0386c6f7d01a3aba79f9400e2e621a8174de2ab436650a9354c2ad0261db1ba8ebe694bc45f4ded8184ed410c53c796286de4e0427a026140f2a183891ac0422c1247e01a793f94ec112dd23b5c460eb72356b060b3cc701c8db7a9d5b7cd311048f3f7694a92da2fd07a000e56277ae75cc9a15e71a81224592e959da5 +ee03c438fc7f8908610430d2cd034cc01a6762d94eb940b5ebe7c227091cc64878d41821ac3e42af3149e45b4543e34660716c2cb49af0b6c03be8ff178a242e3b5daf281aa04e2ddacca16d65a2b065a7c61dbe4bd8160d0a4a005679a4b5f0cd3068e1e092304d2a585c4fb2176bae941fb11374d6bc0272a8513865cd457d +ef6568752b0fc3eafcdcf2f8ac2b1a9f774e53a131eeb37d49559cc60d9caa99d8bc777b9ff4e84702eb83165fa03627d4eb09d286ee42565e87da20bca80643fd26258d3a060206faa9fcc3508ec1537f6578209ac359f544529fd87c471635dd9db44d3ecb9f5d82906c171673ac1d8ab5fb3f1bfd7b3cc2c06cae9b7f6685 +ccec545b7d7f1ea51d8774cdf81a6e8794bc3cd87fbe1a8f8251d9741410b37bc39f6af6ba9efda0cd15243eed15cf11f0b4465e02c38acff963f71fa7220c9d52f64c2fbb8fe044bc6cbaa0895ab8bdedc65a89e0441524cbe28ef9ada6c63d0431583506354b9c2a5176779538bf229b74c8f7feaf5eebdf0825835f33aa8d +e4459658218601a13cc808dc9581fe25f3609976c1f3430c813a2b5dafc902d5440809e75b1e01568bda0d15f8de12f368d7c3a32bac5bd3781f08b0beaba41d4f0541576658fee91042aecf2a450aa0a1612010690c291537a9430de782eb56207da17b54c16e1096eb1f9799ec98910c8557a65d4dff8da6201949a9238715 +f62b559ef3edfe69b2ec36c3af8ec3c36a326409f7d8665ab2a9c26cc28c9daf5350e8164e06966a68e33ceb550504945fa967c6af820a49c4abf6076f94c44340972f524fca03156376720f5bd7a795a4f1d022f1d47cf571d2d4d75b677791bb31bde0a3613d289d4dd1a3da76b9450b56efebc996ff1a59eb3d42da77285d +f397abbfca0f6d6ab5c132ebd18a30c83514b2576c2876e20c6cceb43ae8654f786d9ee5d8ad7b968ff447274d8c082425a477836b59bd27db3920ffd45ce0aec781852cb2a4cba54aa57092b407e2558f27a8a4a43551ab0641b4446fb54845c883dcbefed352cc0f7c557f1597f3e360e8b5d27da51bfd38e7454d509bb19d +fa306da03ce1f59bdcf895d340061798b11295da3078bab00636ae87f758873c32fafa2579563cf2d313c0889664d66e10957e14b0d90af7915cf61151043c1ece2190769ec61bfad26fce27a86094606f734586ed7590500f21ef02db9b19ed5b7b9598396d10135febca8c3e0be26ba0f02e2e54bb6b1ce0a7e424c4cd0e15 +de301f25036944a4f6a8fe11d1d6781cc5e054a09d449a8b1443ee4ad50e9ac8e68e244dd8f8ebc9de0f6e3fd2bd5de7099e17c452631f1962ba400eeb79db5d36dde3f91c7d5780b2ea421b31c2ed57f11cf08c29bfdfe7350e3a87ef477e0d178cec6faa7a4c07519f5fb21661a9ba681906dbc2c2658d9d25bd42e436118d +fc1556bfd9852204f19ac3da8d59b2e889d4232f00afae9dd890b47882c130eeced040fb696842e0635ed0530cddb515054dc514765e9b555f9857cf9bb13cbf5e2fcbc6630a0989c4b2fd565d81e7745851bbffa65ae8ba42591a17c3a44e1d8d60cd549558a4e17bf5343613a2aa4bcb0d086588579865e14b696ef5d03dc5 +dc60b41e021104e87cf43ba8e75dd012c1121bd5c3e682b8efa1f1103a12e27d89c520ff95809fe0bd206ef70775c78a2364d01d91f4afc7c8a8d02a671e14e2cb1df0b14852e7c5134a31b4380788f8b0f37423ed853e3e1c1938ad3f7cec40cfabaea1eef28c8d2b01f6bab2a1d584084897801bd249147e6e0c725eec7ee5 +ceee4f09a49f1de80b4d0dcd33eb56e8ea3d85c1a2e5e856323c03253fb91e2b4347857a86aa1d8484a501238f10b7aaf4adc3c39e42dd04495590c814f6af2c6a07155b1a793cdab9067b7e709bc3d748b10a419fe3f530111444a7b8cf3dba2880ff1bf81a607ea367b5a81b52e16b41080ea36b18a0d3590566af84989945 +f6bc6ad26d71863b9c771160344c9aa98f35be8193d2ee0b47d342a9ded78aaa6c654df6aff01aba55fe283df748f12afb40f6556d4370a9a987b98e2b330c9576f76821afb9103f249af8d6544c76b94d0e19b710465b5a8732ebabce87869a115f7cd88e7c01dbbb79c9cc04d266b9c578a645616e8ae3d8fbae1f80667d15 +f295e2280717c5fccba279523dfac2149274ccbf9e7a6ef99e17444c8d4c89c668d0306cf955ed951b7f60b1ed3470643fedfedc01cba6d66c2f32ab122a83314e84e21e1bb1c1af7ba69fc64faff940e0e2a797aa758f8b2b3bf2e882dffbe2bbe2da6680e622d05ff03a4ef09e88cbc3595179f68779062eb7fffc336ef025 +d607837c88f51a8449b0d1ded1caa08df6bbefad69c55cfbdef7c7f60fc0b2b3851019ef5be975f84238082fc0b1a1b67d595a95ac5e412904cba879d5c67a99479d5132189c9cd21c1b3270f293d1be5b3ae383e4ad9c3e72acfcf357631facbab5adec457b11bf6eac2cd2995a28ae5768ed2dad963bbf28b5794eb082d3b5 +f18ec69d1572c4686076d8f7f96629bc04ac315b0ce5fddf4758aace119fb5562e39535b7f20702ca99e686e229bf951e7bbb6d5cd6919f94235e873c3a47ba36829070b5f34e4153d0649f7c4aa7acf14720036b80362ae1b1063222903e4c62f434ec16c852c926268e739ba62238994dcc6f95c440d4826dcae5a5660168d +d2e36d5dc038ebddb59318b497fa416f76d2ca62fb99f8d9568737430a5946069ef251c20d2311598e9d1055f8ba15b018c28ec131a683d4d26056e67a46216dc529ab933ceae21f762538db6ca6db0473ed9dcc8c1ac880ada42fcf52938bd0cf3c528587a5ed61f4478d185c4b0d4edba24d222b207016be16e43105fc96f5 +e4be11d1cb3926427f6b2d26c43ae756ab16ea299a5c6b4c38eaaef00e0642e2c4cacce642f3be830a8fa93f5bee03a7d738b0a4e4181e0778788cc81b6989549b16ba116cd931446f39be17de2d99896832ff4fa1d4e9d70a15c4a4e7afa4b319cf51fda2ba57572b5656a6333597df083aa8278addc3024c7a21690e7fe305 diff --git a/src/crypto/sha1/sha1.go b/src/crypto/sha1/sha1.go index b799f0d2fb1548..d2ffaac0aeb674 100644 --- a/src/crypto/sha1/sha1.go +++ b/src/crypto/sha1/sha1.go @@ -111,9 +111,6 @@ func New() hash.Hash { if boring.Enabled { return boring.NewSHA1() } - if fips140only.Enabled { - panic("crypto/sha1: use of weak SHA-1 is not allowed in FIPS 140-only mode") - } d := new(digest) d.Reset() return d @@ -124,6 +121,9 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { + if fips140only.Enabled { + return 0, errors.New("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") + } boring.Unreachable() nn = len(p) d.len += uint64(nn) @@ -156,6 +156,10 @@ func (d *digest) Sum(in []byte) []byte { } func (d *digest) checkSum() [Size]byte { + if fips140only.Enabled { + panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") + } + len := d.len // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. var tmp [64 + 8]byte // padding + length buffer @@ -196,6 +200,10 @@ func (d *digest) ConstantTimeSum(in []byte) []byte { } func (d *digest) constSum() [Size]byte { + if fips140only.Enabled { + panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") + } + var length [8]byte l := d.len << 3 for i := uint(0); i < 8; i++ { @@ -262,7 +270,7 @@ func Sum(data []byte) [Size]byte { return boring.SHA1(data) } if fips140only.Enabled { - panic("crypto/sha1: use of weak SHA-1 is not allowed in FIPS 140-only mode") + panic("crypto/sha1: use of SHA-1 is not allowed in FIPS 140-only mode") } var d digest d.Reset() diff --git a/src/crypto/sha3/sha3.go b/src/crypto/sha3/sha3.go index 0f4d7ed4370b5f..a6c5ae55f1ff20 100644 --- a/src/crypto/sha3/sha3.go +++ b/src/crypto/sha3/sha3.go @@ -10,6 +10,7 @@ import ( "crypto" "crypto/internal/fips140/sha3" "hash" + _ "unsafe" ) func init() { @@ -100,6 +101,11 @@ type SHA3 struct { s sha3.Digest } +//go:linkname fips140hash_sha3Unwrap crypto/internal/fips140hash.sha3Unwrap +func fips140hash_sha3Unwrap(sha3 *SHA3) *sha3.Digest { + return &sha3.s +} + // New224 creates a new SHA3-224 hash. func New224() *SHA3 { return &SHA3{*sha3.New224()} diff --git a/src/crypto/subtle/xor_test.go b/src/crypto/subtle/xor_test.go index 2e2169db0aac62..855e54d82bb26f 100644 --- a/src/crypto/subtle/xor_test.go +++ b/src/crypto/subtle/xor_test.go @@ -92,7 +92,7 @@ func BenchmarkXORBytes(b *testing.B) { dst := make([]byte, 1<<15) data0 := make([]byte, 1<<15) data1 := make([]byte, 1<<15) - sizes := []int64{1 << 3, 1 << 7, 1 << 11, 1 << 15} + sizes := []int64{1 << 3, 1 << 7, 1 << 11, 1 << 13, 1 << 15} for _, size := range sizes { b.Run(fmt.Sprintf("%dBytes", size), func(b *testing.B) { s0 := data0[:size] @@ -105,6 +105,26 @@ func BenchmarkXORBytes(b *testing.B) { } } +func BenchmarkXORBytesAlignment(b *testing.B) { + dst := make([]byte, 8+1<<11) + data0 := make([]byte, 8+1<<11) + data1 := make([]byte, 8+1<<11) + sizes := []int64{1 << 3, 1 << 7, 1 << 11} + for _, size := range sizes { + for offset := int64(0); offset < 8; offset++ { + b.Run(fmt.Sprintf("%dBytes%dOffset", size, offset), func(b *testing.B) { + d := dst[offset : offset+size] + s0 := data0[offset : offset+size] + s1 := data1[offset : offset+size] + b.SetBytes(int64(size)) + for i := 0; i < b.N; i++ { + XORBytes(d, s0, s1) + } + }) + } + } +} + func mustPanic(t *testing.T, expected string, f func()) { t.Helper() defer func() { diff --git a/src/crypto/tls/bogo_config.json b/src/crypto/tls/bogo_config.json index 1c313ec81e160f..6472512158c444 100644 --- a/src/crypto/tls/bogo_config.json +++ b/src/crypto/tls/bogo_config.json @@ -42,6 +42,7 @@ "*-SignDefault-*": "TODO, partially it encodes BoringSSL defaults, partially we might be missing some implicit behavior of a missing flag", + "V2ClientHello-*": "We don't support SSLv2", "SendV2ClientHello*": "We don't support SSLv2", "*QUIC*": "No QUIC support", "Compliance-fips*": "No FIPS", @@ -246,5 +247,8 @@ 25, 29, 4588 - ] + ], + "ErrorMap": { + ":ECH_REJECTED:": "tls: server rejected ECH" + } } diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index f98d24b879b889..d6942d2ef14a27 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -456,7 +456,7 @@ type ClientHelloInfo struct { SupportedVersions []uint16 // Extensions lists the IDs of the extensions presented by the client - // in the client hello. + // in the ClientHello. Extensions []uint16 // Conn is the underlying net.Conn for the connection. Do not read @@ -821,7 +821,7 @@ type Config struct { // EncryptedClientHelloRejectionVerify, if not nil, is called when ECH is // rejected by the remote server, in order to verify the ECH provider - // certificate in the outer Client Hello. If it returns a non-nil error, the + // certificate in the outer ClientHello. If it returns a non-nil error, the // handshake is aborted and that error results. // // On the server side this field is not used. diff --git a/src/crypto/tls/ech.go b/src/crypto/tls/ech.go index 55d52179c2c823..d9795b4ee2e291 100644 --- a/src/crypto/tls/ech.go +++ b/src/crypto/tls/ech.go @@ -378,7 +378,7 @@ func decodeInnerClientHello(outer *clientHelloMsg, encoded []byte) (*clientHello } if !bytes.Equal(inner.encryptedClientHello, []byte{uint8(innerECHExt)}) { - return nil, errors.New("tls: client sent invalid encrypted_client_hello extension") + return nil, errInvalidECHExt } if len(inner.supportedVersions) != 1 || (len(inner.supportedVersions) >= 1 && inner.supportedVersions[0] != VersionTLS13) { @@ -481,6 +481,7 @@ func (e *ECHRejectionError) Error() string { } var errMalformedECHExt = errors.New("tls: malformed encrypted_client_hello extension") +var errInvalidECHExt = errors.New("tls: client sent invalid encrypted_client_hello extension") type echExtType uint8 @@ -507,7 +508,7 @@ func parseECHExt(ext []byte) (echType echExtType, cs echCipher, configID uint8, return echType, cs, 0, nil, nil, nil } if echType != outerECHExt { - err = errMalformedECHExt + err = errInvalidECHExt return } if !s.ReadUint16(&cs.KDFID) { @@ -549,8 +550,13 @@ func marshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloKey) ([ func (c *Conn) processECHClientHello(outer *clientHelloMsg) (*clientHelloMsg, *echServerContext, error) { echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello) if err != nil { - c.sendAlert(alertDecodeError) - return nil, nil, errors.New("tls: client sent invalid encrypted_client_hello extension") + if errors.Is(err, errInvalidECHExt) { + c.sendAlert(alertIllegalParameter) + } else { + c.sendAlert(alertDecodeError) + } + + return nil, nil, errInvalidECHExt } if echType == innerECHExt { @@ -597,7 +603,7 @@ func (c *Conn) processECHClientHello(outer *clientHelloMsg) (*clientHelloMsg, *e echInner, err := decodeInnerClientHello(outer, encodedInner) if err != nil { c.sendAlert(alertIllegalParameter) - return nil, nil, errors.New("tls: client sent invalid encrypted_client_hello extension") + return nil, nil, errInvalidECHExt } c.echAccepted = true diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index ecc62ff2edefc0..38bd417a0dca72 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -260,6 +260,7 @@ type echClientContext struct { kdfID uint16 aeadID uint16 echRejected bool + retryConfigs []byte } func (c *Conn) clientHandshake(ctx context.Context) (err error) { @@ -944,7 +945,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { } // checkALPN ensure that the server's choice of ALPN protocol is compatible with -// the protocols that we advertised in the Client Hello. +// the protocols that we advertised in the ClientHello. func checkALPN(clientProtos []string, serverProto string, quic bool) error { if serverProto == "" { if quic && len(clientProtos) > 0 { diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go index bb164bba553f43..bc54475fa4b6d2 100644 --- a/src/crypto/tls/handshake_client_test.go +++ b/src/crypto/tls/handshake_client_test.go @@ -856,6 +856,7 @@ func testResumption(t *testing.T, version uint16) { MaxVersion: version, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, Certificates: testCertificates, + Time: testTime, } issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer) @@ -872,6 +873,7 @@ func testResumption(t *testing.T, version uint16) { ClientSessionCache: NewLRUClientSessionCache(32), RootCAs: rootCAs, ServerName: "example.golang", + Time: testTime, } testResumeState := func(test string, didResume bool) { @@ -918,7 +920,7 @@ func testResumption(t *testing.T, version uint16) { // An old session ticket is replaced with a ticket encrypted with a fresh key. ticket = getTicket() - serverConfig.Time = func() time.Time { return time.Now().Add(24*time.Hour + time.Minute) } + serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) } testResumeState("ResumeWithOldTicket", true) if bytes.Equal(ticket, getTicket()) { t.Fatal("old first ticket matches the fresh one") @@ -926,13 +928,13 @@ func testResumption(t *testing.T, version uint16) { // Once the session master secret is expired, a full handshake should occur. ticket = getTicket() - serverConfig.Time = func() time.Time { return time.Now().Add(24*8*time.Hour + time.Minute) } + serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + time.Minute) } testResumeState("ResumeWithExpiredTicket", false) if bytes.Equal(ticket, getTicket()) { t.Fatal("expired first ticket matches the fresh one") } - serverConfig.Time = func() time.Time { return time.Now() } // reset the time back + serverConfig.Time = testTime // reset the time back key1 := randomKey() serverConfig.SetSessionTicketKeys([][32]byte{key1}) @@ -949,11 +951,11 @@ func testResumption(t *testing.T, version uint16) { testResumeState("KeyChangeFinish", true) // Age the session ticket a bit, but not yet expired. - serverConfig.Time = func() time.Time { return time.Now().Add(24*time.Hour + time.Minute) } + serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) } testResumeState("OldSessionTicket", true) ticket = getTicket() // Expire the session ticket, which would force a full handshake. - serverConfig.Time = func() time.Time { return time.Now().Add(24*8*time.Hour + time.Minute) } + serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + 2*time.Minute) } testResumeState("ExpiredSessionTicket", false) if bytes.Equal(ticket, getTicket()) { t.Fatal("new ticket wasn't provided after old ticket expired") @@ -961,7 +963,7 @@ func testResumption(t *testing.T, version uint16) { // Age the session ticket a bit at a time, but don't expire it. d := 0 * time.Hour - serverConfig.Time = func() time.Time { return time.Now().Add(d) } + serverConfig.Time = func() time.Time { return testTime().Add(d) } deleteTicket() testResumeState("GetFreshSessionTicket", false) for i := 0; i < 13; i++ { @@ -972,7 +974,7 @@ func testResumption(t *testing.T, version uint16) { // handshake occurs for TLS 1.2. Resumption should still occur for // TLS 1.3 since the client should be using a fresh ticket sent over // by the server. - d += 12 * time.Hour + d += 12*time.Hour + time.Minute if version == VersionTLS13 { testResumeState("ExpiredSessionTicket", true) } else { @@ -988,6 +990,7 @@ func testResumption(t *testing.T, version uint16) { MaxVersion: version, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, Certificates: testCertificates, + Time: testTime, } serverConfig.SetSessionTicketKeys([][32]byte{key2}) @@ -1013,6 +1016,7 @@ func testResumption(t *testing.T, version uint16) { CurvePreferences: []CurveID{CurveP521, CurveP384, CurveP256}, MaxVersion: version, Certificates: testCertificates, + Time: testTime, } testResumeState("InitialHandshake", false) testResumeState("WithHelloRetryRequest", true) @@ -1022,6 +1026,7 @@ func testResumption(t *testing.T, version uint16) { MaxVersion: version, CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, Certificates: testCertificates, + Time: testTime, } } @@ -1743,6 +1748,7 @@ func testVerifyConnection(t *testing.T, version uint16) { serverConfig := &Config{ MaxVersion: version, Certificates: testCertificates, + Time: testTime, ClientCAs: rootCAs, NextProtos: []string{"protocol1"}, } @@ -1756,6 +1762,7 @@ func testVerifyConnection(t *testing.T, version uint16) { RootCAs: rootCAs, ServerName: "example.golang", Certificates: testCertificates, + Time: testTime, NextProtos: []string{"protocol1"}, } test.configureClient(clientConfig, &clientCalled) @@ -1799,8 +1806,6 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) { rootCAs := x509.NewCertPool() rootCAs.AddCert(issuer) - now := func() time.Time { return time.Unix(1476984729, 0) } - sentinelErr := errors.New("TestVerifyPeerCertificate") verifyPeerCertificateCallback := func(called *bool, rawCerts [][]byte, validatedChains [][]*x509.Certificate) error { @@ -2046,7 +2051,7 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) { config.ServerName = "example.golang" config.ClientAuth = RequireAndVerifyClientCert config.ClientCAs = rootCAs - config.Time = now + config.Time = testTime config.MaxVersion = version config.Certificates = make([]Certificate, 1) config.Certificates[0].Certificate = [][]byte{testRSA2048Certificate} @@ -2064,7 +2069,7 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) { config.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} config.ServerName = "example.golang" config.RootCAs = rootCAs - config.Time = now + config.Time = testTime config.MaxVersion = version test.configureClient(config, &clientCalled) clientErr := Client(c, config).Handshake() @@ -2379,7 +2384,7 @@ func testGetClientCertificate(t *testing.T, version uint16) { serverConfig.RootCAs = x509.NewCertPool() serverConfig.RootCAs.AddCert(issuer) serverConfig.ClientCAs = serverConfig.RootCAs - serverConfig.Time = func() time.Time { return time.Unix(1476984729, 0) } + serverConfig.Time = testTime serverConfig.MaxVersion = version clientConfig := testConfig.Clone() @@ -2562,6 +2567,7 @@ func testResumptionKeepsOCSPAndSCT(t *testing.T, ver uint16) { ClientSessionCache: NewLRUClientSessionCache(32), ServerName: "example.golang", RootCAs: roots, + Time: testTime, } serverConfig := testConfig.Clone() serverConfig.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go index 38c6025db74ee8..c0396e75796add 100644 --- a/src/crypto/tls/handshake_client_tls13.go +++ b/src/crypto/tls/handshake_client_tls13.go @@ -85,7 +85,6 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } } - var echRetryConfigList []byte if hs.echContext != nil { confTranscript := cloneHash(hs.echContext.innerTranscript, hs.suite.hash) confTranscript.Write(hs.serverHello.original[:30]) @@ -114,9 +113,6 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } } else { hs.echContext.echRejected = true - // If the server sent us retry configs, we'll return these to - // the user so they can update their Config. - echRetryConfigList = hs.serverHello.encryptedClientHello } } @@ -155,7 +151,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error { if hs.echContext != nil && hs.echContext.echRejected { c.sendAlert(alertECHRequired) - return &ECHRejectionError{echRetryConfigList} + return &ECHRejectionError{hs.echContext.retryConfigs} } c.isHandshakeComplete.Store(true) @@ -601,9 +597,13 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error { return errors.New("tls: server accepted 0-RTT with the wrong ALPN") } } - if hs.echContext != nil && !hs.echContext.echRejected && encryptedExtensions.echRetryConfigs != nil { - c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: server sent encrypted client hello retry configs after accepting encrypted client hello") + if hs.echContext != nil { + if hs.echContext.echRejected { + hs.echContext.retryConfigs = encryptedExtensions.echRetryConfigs + } else if encryptedExtensions.echRetryConfigs != nil { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: server sent encrypted client hello retry configs after accepting encrypted client hello") + } } return nil diff --git a/src/crypto/tls/handshake_messages.go b/src/crypto/tls/handshake_messages.go index fa00d7b741100e..6c6141c421ea34 100644 --- a/src/crypto/tls/handshake_messages.go +++ b/src/crypto/tls/handshake_messages.go @@ -97,7 +97,7 @@ type clientHelloMsg struct { pskBinders [][]byte quicTransportParameters []byte encryptedClientHello []byte - // extensions are only populated on the servers-ide of a handshake + // extensions are only populated on the server-side of a handshake extensions []uint16 } diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go index 29a802d54b2dda..f533023afba5b9 100644 --- a/src/crypto/tls/handshake_server_test.go +++ b/src/crypto/tls/handshake_server_test.go @@ -519,6 +519,7 @@ func testCrossVersionResume(t *testing.T, version uint16) { serverConfig := &Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Certificates: testConfig.Certificates, + Time: testTime, } clientConfig := &Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, @@ -526,6 +527,7 @@ func testCrossVersionResume(t *testing.T, version uint16) { ClientSessionCache: NewLRUClientSessionCache(1), ServerName: "servername", MinVersion: VersionTLS12, + Time: testTime, } // Establish a session at TLS 1.3. diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 3552d89ba3bc6f..b6d455cd397e31 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -280,7 +280,7 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid X25519MLKEM768 client key share") } - ciphertext, mlkemSharedSecret := k.Encapsulate() + mlkemSharedSecret, ciphertext := k.Encapsulate() // draft-kwiatkowski-tls-ecdhe-mlkem-02, Section 3.1.3: "For // X25519MLKEM768, the shared secret is the concatenation of the ML-KEM // shared secret and the X25519 shared secret. The shared secret is 64 @@ -949,12 +949,7 @@ func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool { } // Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9. - for _, pskMode := range hs.clientHello.pskModes { - if pskMode == pskModeDHE { - return true - } - } - return false + return slices.Contains(hs.clientHello.pskModes, pskModeDHE) } func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go index 5a9c24fb83ee6a..ea8ac6fc837ae7 100644 --- a/src/crypto/tls/handshake_test.go +++ b/src/crypto/tls/handshake_test.go @@ -522,6 +522,11 @@ func fromHex(s string) []byte { return b } +// testTime is 2016-10-20T17:32:09.000Z, which is within the validity period of +// [testRSACertificate], [testRSACertificateIssuer], [testRSA2048Certificate], +// [testRSA2048CertificateIssuer], and [testECDSACertificate]. +var testTime = func() time.Time { return time.Unix(1476984729, 0) } + var testRSACertificate = fromHex("3082024b308201b4a003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301a310b3009060355040a1302476f310b300906035504031302476f30819f300d06092a864886f70d010101050003818d0030818902818100db467d932e12270648bc062821ab7ec4b6a25dfe1e5245887a3647a5080d92425bc281c0be97799840fb4f6d14fd2b138bc2a52e67d8d4099ed62238b74a0b74732bc234f1d193e596d9747bf3589f6c613cc0b041d4d92b2b2423775b1c3bbd755dce2054cfa163871d1e24c4f31d1a508baab61443ed97a77562f414c852d70203010001a38193308190300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302300c0603551d130101ff0402300030190603551d0e041204109f91161f43433e49a6de6db680d79f60301b0603551d230414301280104813494d137e1631bba301d5acab6e7b30190603551d1104123010820e6578616d706c652e676f6c616e67300d06092a864886f70d01010b0500038181009d30cc402b5b50a061cbbae55358e1ed8328a9581aa938a495a1ac315a1a84663d43d32dd90bf297dfd320643892243a00bccf9c7db74020015faad3166109a276fd13c3cce10c5ceeb18782f16c04ed73bbb343778d0c1cf10fa1d8408361c94c722b9daedb4606064df4c1b33ec0d1bd42d4dbfe3d1360845c21d33be9fae7") var testRSACertificateIssuer = fromHex("3082021930820182a003020102020900ca5e4e811a965964300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f7430819f300d06092a864886f70d010101050003818d0030818902818100d667b378bb22f34143b6cd2008236abefaf2852adf3ab05e01329e2c14834f5105df3f3073f99dab5442d45ee5f8f57b0111c8cb682fbb719a86944eebfffef3406206d898b8c1b1887797c9c5006547bb8f00e694b7a063f10839f269f2c34fff7a1f4b21fbcd6bfdfb13ac792d1d11f277b5c5b48600992203059f2a8f8cc50203010001a35d305b300e0603551d0f0101ff040403020204301d0603551d250416301406082b0601050507030106082b06010505070302300f0603551d130101ff040530030101ff30190603551d0e041204104813494d137e1631bba301d5acab6e7b300d06092a864886f70d01010b050003818100c1154b4bab5266221f293766ae4138899bd4c5e36b13cee670ceeaa4cbdf4f6679017e2fe649765af545749fe4249418a56bd38a04b81e261f5ce86b8d5c65413156a50d12449554748c59a30c515bc36a59d38bddf51173e899820b282e40aa78c806526fd184fb6b4cf186ec728edffa585440d2b3225325f7ab580e87dd76") diff --git a/src/crypto/tls/tls.go b/src/crypto/tls/tls.go index f3089f0ed68dda..c64201abc1f406 100644 --- a/src/crypto/tls/tls.go +++ b/src/crypto/tls/tls.go @@ -4,6 +4,15 @@ // Package tls partially implements TLS 1.2, as specified in RFC 5246, // and TLS 1.3, as specified in RFC 8446. +// +// # FIPS 140-3 mode +// +// When the program is in [FIPS 140-3 mode], this package behaves as if +// only protocol versions, cipher suites, signature algorithms, and +// key exchange algorithms approved by NIST SP 800-52r2 are implemented. +// Others are silently ignored and not negotiated. +// +// [FIPS 140-3 mode]: https://go.dev/doc/security/fips140 package tls // BUG(agl): The crypto/tls package only implements some countermeasures diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index 51cd2b91bdb754..76a9a222a94bd7 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -1158,8 +1158,6 @@ func TestConnectionState(t *testing.T) { rootCAs := x509.NewCertPool() rootCAs.AddCert(issuer) - now := func() time.Time { return time.Unix(1476984729, 0) } - const alpnProtocol = "golang" const serverName = "example.golang" var scts = [][]byte{[]byte("dummy sct 1"), []byte("dummy sct 2")} @@ -1175,7 +1173,7 @@ func TestConnectionState(t *testing.T) { } t.Run(name, func(t *testing.T) { config := &Config{ - Time: now, + Time: testTime, Rand: zeroSource{}, Certificates: make([]Certificate, 1), MaxVersion: v, @@ -1810,7 +1808,7 @@ func testVerifyCertificates(t *testing.T, version uint16) { var serverVerifyPeerCertificates, clientVerifyPeerCertificates bool clientConfig := testConfig.Clone() - clientConfig.Time = func() time.Time { return time.Unix(1476984729, 0) } + clientConfig.Time = testTime clientConfig.MaxVersion = version clientConfig.MinVersion = version clientConfig.RootCAs = rootCAs diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go index a6972c0c09dfe2..497ba6e824cd93 100644 --- a/src/crypto/x509/internal/macos/security.go +++ b/src/crypto/x509/internal/macos/security.go @@ -20,37 +20,6 @@ import ( // Based on https://opensource.apple.com/source/Security/Security-59306.41.2/base/Security.h -type SecTrustSettingsResult int32 - -const ( - SecTrustSettingsResultInvalid SecTrustSettingsResult = iota - SecTrustSettingsResultTrustRoot - SecTrustSettingsResultTrustAsRoot - SecTrustSettingsResultDeny - SecTrustSettingsResultUnspecified -) - -type SecTrustResultType int32 - -const ( - SecTrustResultInvalid SecTrustResultType = iota - SecTrustResultProceed - SecTrustResultConfirm // deprecated - SecTrustResultDeny - SecTrustResultUnspecified - SecTrustResultRecoverableTrustFailure - SecTrustResultFatalTrustFailure - SecTrustResultOtherError -) - -type SecTrustSettingsDomain int32 - -const ( - SecTrustSettingsDomainUser SecTrustSettingsDomain = iota - SecTrustSettingsDomainAdmin - SecTrustSettingsDomainSystem -) - const ( // various macOS error codes that can be returned from // SecTrustEvaluateWithError that we can map to Go cert @@ -69,54 +38,6 @@ func (s OSStatus) Error() string { return s.call + " error: " + strconv.Itoa(int(s.status)) } -// Dictionary keys are defined as build-time strings with CFSTR, but the Go -// linker's internal linking mode can't handle CFSTR relocations. Create our -// own dynamic strings instead and just never release them. -// -// Note that this might be the only thing that can break over time if -// these values change, as the ABI arguably requires using the strings -// pointed to by the symbols, not values that happen to be equal to them. - -var SecTrustSettingsResultKey = StringToCFString("kSecTrustSettingsResult") -var SecTrustSettingsPolicy = StringToCFString("kSecTrustSettingsPolicy") -var SecTrustSettingsPolicyString = StringToCFString("kSecTrustSettingsPolicyString") -var SecPolicyOid = StringToCFString("SecPolicyOid") -var SecPolicyAppleSSL = StringToCFString("1.2.840.113635.100.1.3") // defined by POLICYMACRO - -var ErrNoTrustSettings = errors.New("no trust settings found") - -const errSecNoTrustSettings = -25263 - -//go:cgo_import_dynamic x509_SecTrustSettingsCopyCertificates SecTrustSettingsCopyCertificates "/System/Library/Frameworks/Security.framework/Versions/A/Security" - -func SecTrustSettingsCopyCertificates(domain SecTrustSettingsDomain) (certArray CFRef, err error) { - ret := syscall(abi.FuncPCABI0(x509_SecTrustSettingsCopyCertificates_trampoline), uintptr(domain), - uintptr(unsafe.Pointer(&certArray)), 0, 0, 0, 0) - if int32(ret) == errSecNoTrustSettings { - return 0, ErrNoTrustSettings - } else if ret != 0 { - return 0, OSStatus{"SecTrustSettingsCopyCertificates", int32(ret)} - } - return certArray, nil -} -func x509_SecTrustSettingsCopyCertificates_trampoline() - -const errSecItemNotFound = -25300 - -//go:cgo_import_dynamic x509_SecTrustSettingsCopyTrustSettings SecTrustSettingsCopyTrustSettings "/System/Library/Frameworks/Security.framework/Versions/A/Security" - -func SecTrustSettingsCopyTrustSettings(cert CFRef, domain SecTrustSettingsDomain) (trustSettings CFRef, err error) { - ret := syscall(abi.FuncPCABI0(x509_SecTrustSettingsCopyTrustSettings_trampoline), uintptr(cert), uintptr(domain), - uintptr(unsafe.Pointer(&trustSettings)), 0, 0, 0) - if int32(ret) == errSecItemNotFound { - return 0, ErrNoTrustSettings - } else if ret != 0 { - return 0, OSStatus{"SecTrustSettingsCopyTrustSettings", int32(ret)} - } - return trustSettings, nil -} -func x509_SecTrustSettingsCopyTrustSettings_trampoline() - //go:cgo_import_dynamic x509_SecTrustCreateWithCertificates SecTrustCreateWithCertificates "/System/Library/Frameworks/Security.framework/Versions/A/Security" func SecTrustCreateWithCertificates(certs CFRef, policies CFRef) (CFRef, error) { @@ -184,19 +105,6 @@ func SecTrustEvaluate(trustObj CFRef) (CFRef, error) { } func x509_SecTrustEvaluate_trampoline() -//go:cgo_import_dynamic x509_SecTrustGetResult SecTrustGetResult "/System/Library/Frameworks/Security.framework/Versions/A/Security" - -func SecTrustGetResult(trustObj CFRef, result CFRef) (CFRef, CFRef, error) { - var chain, info CFRef - ret := syscall(abi.FuncPCABI0(x509_SecTrustGetResult_trampoline), uintptr(trustObj), uintptr(unsafe.Pointer(&result)), - uintptr(unsafe.Pointer(&chain)), uintptr(unsafe.Pointer(&info)), 0, 0) - if int32(ret) != 0 { - return 0, 0, OSStatus{"SecTrustGetResult", int32(ret)} - } - return chain, info, nil -} -func x509_SecTrustGetResult_trampoline() - //go:cgo_import_dynamic x509_SecTrustEvaluateWithError SecTrustEvaluateWithError "/System/Library/Frameworks/Security.framework/Versions/A/Security" func SecTrustEvaluateWithError(trustObj CFRef) (int, error) { diff --git a/src/crypto/x509/internal/macos/security.s b/src/crypto/x509/internal/macos/security.s index ed726f1127c8ce..dc630eccb7fe41 100644 --- a/src/crypto/x509/internal/macos/security.s +++ b/src/crypto/x509/internal/macos/security.s @@ -9,10 +9,6 @@ // The trampolines are ABIInternal as they are address-taken in // Go code. -TEXT ·x509_SecTrustSettingsCopyCertificates_trampoline(SB),NOSPLIT,$0-0 - JMP x509_SecTrustSettingsCopyCertificates(SB) -TEXT ·x509_SecTrustSettingsCopyTrustSettings_trampoline(SB),NOSPLIT,$0-0 - JMP x509_SecTrustSettingsCopyTrustSettings(SB) TEXT ·x509_SecTrustCreateWithCertificates_trampoline(SB),NOSPLIT,$0-0 JMP x509_SecTrustCreateWithCertificates(SB) TEXT ·x509_SecCertificateCreateWithData_trampoline(SB),NOSPLIT,$0-0 @@ -23,8 +19,6 @@ TEXT ·x509_SecTrustSetVerifyDate_trampoline(SB),NOSPLIT,$0-0 JMP x509_SecTrustSetVerifyDate(SB) TEXT ·x509_SecTrustEvaluate_trampoline(SB),NOSPLIT,$0-0 JMP x509_SecTrustEvaluate(SB) -TEXT ·x509_SecTrustGetResult_trampoline(SB),NOSPLIT,$0-0 - JMP x509_SecTrustGetResult(SB) TEXT ·x509_SecTrustEvaluateWithError_trampoline(SB),NOSPLIT,$0-0 JMP x509_SecTrustEvaluateWithError(SB) TEXT ·x509_SecTrustGetCertificateCount_trampoline(SB),NOSPLIT,$0-0 diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go index 008c7028f4e4c4..a5851845164d10 100644 --- a/src/crypto/x509/name_constraints_test.go +++ b/src/crypto/x509/name_constraints_test.go @@ -1607,6 +1607,23 @@ var nameConstraintsTests = []nameConstraintsTest{ leaf: leafSpec{sans: []string{"dns:.example.com"}}, expectedError: "cannot parse dnsName \".example.com\"", }, + // #86: URIs with IPv6 addresses with zones and ports are rejected + { + roots: []constraintsSpec{ + { + ok: []string{"uri:example.com"}, + }, + }, + intermediates: [][]constraintsSpec{ + { + {}, + }, + }, + leaf: leafSpec{ + sans: []string{"uri:http://[2006:abcd::1%25.example.com]:16/"}, + }, + expectedError: "URI with IP", + }, } func makeConstraintsCACert(constraints constraintsSpec, name string, key *ecdsa.PrivateKey, parent *Certificate, parentKey *ecdsa.PrivateKey) (*Certificate, error) { diff --git a/src/crypto/x509/pkcs1.go b/src/crypto/x509/pkcs1.go index ca23358c8c4610..68aa8dd980c10e 100644 --- a/src/crypto/x509/pkcs1.go +++ b/src/crypto/x509/pkcs1.go @@ -72,7 +72,9 @@ func ParsePKCS1PrivateKey(der []byte) (*rsa.PrivateKey, error) { } if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 || - priv.Dp.Sign() <= 0 || priv.Dq.Sign() <= 0 || priv.Qinv.Sign() <= 0 { + priv.Dp != nil && priv.Dp.Sign() <= 0 || + priv.Dq != nil && priv.Dq.Sign() <= 0 || + priv.Qinv != nil && priv.Qinv.Sign() <= 0 { return nil, errors.New("x509: private key contains zero or negative value") } diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index d2384f56653f86..5fe93c6124a989 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -13,6 +13,7 @@ import ( "iter" "maps" "net" + "net/netip" "net/url" "reflect" "runtime" @@ -465,8 +466,10 @@ func matchURIConstraint(uri *url.URL, constraint string) (bool, error) { } } - if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") || - net.ParseIP(host) != nil { + // netip.ParseAddr will reject the URI IPv6 literal form "[...]", so we + // check if _either_ the string parses as an IP, or if it is enclosed in + // square brackets. + if _, err := netip.ParseAddr(host); err == nil || (strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]")) { return false, fmt.Errorf("URI with IP (%q) cannot be matched against constraints", uri.String()) } diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go index 941ea572e69f26..f67f40778b930b 100644 --- a/src/crypto/x509/x509_test.go +++ b/src/crypto/x509/x509_test.go @@ -59,6 +59,32 @@ func TestParsePKCS1PrivateKey(t *testing.T) { if _, err := ParsePKCS1PrivateKey(data); err == nil { t.Errorf("parsing invalid private key did not result in an error") } + + // A partial key without CRT values should still parse. + b, _ := asn1.Marshal(struct { + Version int + N *big.Int + E int + D *big.Int + P *big.Int + Q *big.Int + }{ + N: priv.N, + E: priv.PublicKey.E, + D: priv.D, + P: priv.Primes[0], + Q: priv.Primes[1], + }) + p2, err := ParsePKCS1PrivateKey(b) + if err != nil { + t.Fatalf("parsing partial private key resulted in an error: %v", err) + } + if !p2.Equal(priv) { + t.Errorf("partial private key did not match original key") + } + if p2.Precomputed.Dp == nil || p2.Precomputed.Dq == nil || p2.Precomputed.Qinv == nil { + t.Errorf("precomputed values not recomputed") + } } func TestPKCS1MismatchPublicKeyFormat(t *testing.T) { diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index db1d8b3c6b65ef..74b9bf550249c7 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -1374,8 +1374,7 @@ func TestConnQuery(t *testing.T) { db := newTestDB(t, "people") defer closeDB(t, db) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() conn, err := db.Conn(ctx) if err != nil { t.Fatal(err) @@ -1402,8 +1401,7 @@ func TestConnRaw(t *testing.T) { db := newTestDB(t, "people") defer closeDB(t, db) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() conn, err := db.Conn(ctx) if err != nil { t.Fatal(err) @@ -1518,8 +1516,7 @@ func TestInvalidNilValues(t *testing.T) { db := newTestDB(t, "people") defer closeDB(t, db) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() conn, err := db.Conn(ctx) if err != nil { t.Fatal(err) @@ -1547,8 +1544,7 @@ func TestConnTx(t *testing.T) { db := newTestDB(t, "people") defer closeDB(t, db) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() conn, err := db.Conn(ctx) if err != nil { t.Fatal(err) @@ -2793,8 +2789,7 @@ func TestManyErrBadConn(t *testing.T) { // Conn db = manyErrBadConnSetup() defer closeDB(t, db) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() conn, err := db.Conn(ctx) if err != nil { t.Fatal(err) @@ -2935,8 +2930,7 @@ func TestConnExpiresFreshOutOfPool(t *testing.T) { } defer func() { nowFunc = time.Now }() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() db := newTestDB(t, "magicquery") defer closeDB(t, db) @@ -3786,8 +3780,7 @@ func TestIssue20647(t *testing.T) { db := newTestDB(t, "people") defer closeDB(t, db) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() conn, err := db.Conn(ctx) if err != nil { @@ -4142,9 +4135,7 @@ func TestNamedValueChecker(t *testing.T) { } defer db.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - + ctx := t.Context() _, err = db.ExecContext(ctx, "WIPE") if err != nil { t.Fatal("exec wipe", err) @@ -4192,9 +4183,7 @@ func TestNamedValueCheckerSkip(t *testing.T) { } defer db.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - + ctx := t.Context() _, err = db.ExecContext(ctx, "WIPE") if err != nil { t.Fatal("exec wipe", err) @@ -4305,8 +4294,7 @@ func TestQueryExecContextOnly(t *testing.T) { } defer db.Close() - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + ctx := t.Context() conn, err := db.Conn(ctx) if err != nil { diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index aa523c3fae910c..89bd70b5b2a87c 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -209,10 +209,13 @@ type Symbol struct { Name string Info, Other byte - // These fields are used for symbol versioning - // and are present only for the dynamic symbol table. - VersionIndex int16 - VersionFlags SymbolVersionFlag + // HasVersion reports whether the symbol has any version information. + // This will only be true for the dynamic symbol table. + HasVersion bool + // VersionIndex is the symbol's version index. + // Use the methods of the [VersionIndex] type to access it. + // This field is only meaningful if HasVersion is true. + VersionIndex VersionIndex Section SectionIndex Value, Size uint64 @@ -666,7 +669,6 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { symbols[i].Name = str symbols[i].Info = sym.Info symbols[i].Other = sym.Other - symbols[i].VersionIndex = -1 symbols[i].Section = SectionIndex(sym.Shndx) symbols[i].Value = uint64(sym.Value) symbols[i].Size = uint64(sym.Size) @@ -714,7 +716,6 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { symbols[i].Name = str symbols[i].Info = sym.Info symbols[i].Other = sym.Other - symbols[i].VersionIndex = -1 symbols[i].Section = SectionIndex(sym.Shndx) symbols[i].Value = sym.Value symbols[i].Size = sym.Size @@ -1455,9 +1456,13 @@ func (f *File) DynamicSymbols() ([]Symbol, error) { if err != nil { return nil, err } - if f.gnuVersionInit(str) { + hasVersions, err := f.gnuVersionInit(str) + if err != nil { + return nil, err + } + if hasVersions { for i := range sym { - sym[i].VersionIndex, sym[i].Version, sym[i].Library, sym[i].VersionFlags = f.gnuVersion(i) + sym[i].HasVersion, sym[i].VersionIndex, sym[i].Version, sym[i].Library = f.gnuVersion(i) } } return sym, nil @@ -1478,58 +1483,85 @@ func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { if err != nil { return nil, err } - f.gnuVersionInit(str) + if _, err := f.gnuVersionInit(str); err != nil { + return nil, err + } var all []ImportedSymbol for i, s := range sym { if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF { all = append(all, ImportedSymbol{Name: s.Name}) sym := &all[len(all)-1] - _, sym.Version, sym.Library, _ = f.gnuVersion(i) + _, _, sym.Version, sym.Library = f.gnuVersion(i) } } return all, nil } -type SymbolVersionFlag byte +// VersionIndex is the type of a [Symbol] version index. +type VersionIndex uint16 -const ( - VerFlagNone SymbolVersionFlag = 0x0 // No flags. - VerFlagLocal SymbolVersionFlag = 0x1 // Symbol has local scope. - VerFlagGlobal SymbolVersionFlag = 0x2 // Symbol has global scope. - VerFlagHidden SymbolVersionFlag = 0x4 // Symbol is hidden. -) +// IsHidden reports whether the symbol is hidden within the version. +// This means that the symbol can only be seen by specifying the exact version. +func (vi VersionIndex) IsHidden() bool { + return vi&0x8000 != 0 +} + +// Index returns the version index. +// If this is the value 0, it means that the symbol is local, +// and is not visible externally. +// If this is the value 1, it means that the symbol is in the base version, +// and has no specific version; it may or may not match a +// [DynamicVersion.Index] in the slice returned by [File.DynamicVersions]. +// Other values will match either [DynamicVersion.Index] +// in the slice returned by [File.DynamicVersions], +// or [DynamicVersionDep.Index] in the Needs field +// of the elements of the slice returned by [File.DynamicVersionNeeds]. +// In general, a defined symbol will have an index referring +// to DynamicVersions, and an undefined symbol will have an index +// referring to some version in DynamicVersionNeeds. +func (vi VersionIndex) Index() uint16 { + return uint16(vi & 0x7fff) +} // DynamicVersion is a version defined by a dynamic object. +// This describes entries in the ELF SHT_GNU_verdef section. +// We assume that the vd_version field is 1. +// Note that the name of the version appears here; +// it is not in the first Deps entry as it is in the ELF file. type DynamicVersion struct { - Version uint16 // Version of data structure. - Flags DynamicVersionFlag - Index uint16 // Version index. - Deps []string // Dependencies. + Name string // Name of version defined by this index. + Index uint16 // Version index. + Flags DynamicVersionFlag + Deps []string // Names of versions that this version depends upon. } +// DynamicVersionNeed describes a shared library needed by a dynamic object, +// with a list of the versions needed from that shared library. +// This describes entries in the ELF SHT_GNU_verneed section. +// We assume that the vn_version field is 1. type DynamicVersionNeed struct { - Version uint16 // Version of data structure. - Name string // Shared library name. - Needs []DynamicVersionDep // Dependencies. + Name string // Shared library name. + Needs []DynamicVersionDep // Dependencies. } +// DynamicVersionDep is a version needed from some shared library. type DynamicVersionDep struct { Flags DynamicVersionFlag - Other uint16 // Version index. + Index uint16 // Version index. Dep string // Name of required version. } // dynamicVersions returns version information for a dynamic object. -func (f *File) dynamicVersions(str []byte) bool { +func (f *File) dynamicVersions(str []byte) error { if f.dynVers != nil { // Already initialized. - return true + return nil } // Accumulate verdef information. vd := f.SectionByType(SHT_GNU_VERDEF) if vd == nil { - return false + return nil } d, _ := vd.Data() @@ -1540,12 +1572,20 @@ func (f *File) dynamicVersions(str []byte) bool { break } version := f.ByteOrder.Uint16(d[i : i+2]) + if version != 1 { + return &FormatError{int64(vd.Offset + uint64(i)), "unexpected dynamic version", version} + } flags := DynamicVersionFlag(f.ByteOrder.Uint16(d[i+2 : i+4])) ndx := f.ByteOrder.Uint16(d[i+4 : i+6]) cnt := f.ByteOrder.Uint16(d[i+6 : i+8]) aux := f.ByteOrder.Uint32(d[i+12 : i+16]) next := f.ByteOrder.Uint32(d[i+16 : i+20]) + if cnt == 0 { + return &FormatError{int64(vd.Offset + uint64(i)), "dynamic version has no name", nil} + } + + var name string var depName string var deps []string j := i + int(aux) @@ -1557,16 +1597,20 @@ func (f *File) dynamicVersions(str []byte) bool { vnext := f.ByteOrder.Uint32(d[j+4 : j+8]) depName, _ = getString(str, int(vname)) - deps = append(deps, depName) + if c == 0 { + name = depName + } else { + deps = append(deps, depName) + } j += int(vnext) } dynVers = append(dynVers, DynamicVersion{ - Version: version, - Flags: flags, - Index: ndx, - Deps: deps, + Name: name, + Index: ndx, + Flags: flags, + Deps: deps, }) if next == 0 { @@ -1577,7 +1621,7 @@ func (f *File) dynamicVersions(str []byte) bool { f.dynVers = dynVers - return true + return nil } // DynamicVersions returns version information for a dynamic object. @@ -1587,7 +1631,11 @@ func (f *File) DynamicVersions() ([]DynamicVersion, error) { if err != nil { return nil, err } - if !f.gnuVersionInit(str) { + hasVersions, err := f.gnuVersionInit(str) + if err != nil { + return nil, err + } + if !hasVersions { return nil, errors.New("DynamicVersions: missing version table") } } @@ -1596,16 +1644,16 @@ func (f *File) DynamicVersions() ([]DynamicVersion, error) { } // dynamicVersionNeeds returns version dependencies for a dynamic object. -func (f *File) dynamicVersionNeeds(str []byte) bool { +func (f *File) dynamicVersionNeeds(str []byte) error { if f.dynVerNeeds != nil { // Already initialized. - return true + return nil } // Accumulate verneed information. vn := f.SectionByType(SHT_GNU_VERNEED) if vn == nil { - return false + return nil } d, _ := vn.Data() @@ -1617,7 +1665,7 @@ func (f *File) dynamicVersionNeeds(str []byte) bool { } vers := f.ByteOrder.Uint16(d[i : i+2]) if vers != 1 { - break + return &FormatError{int64(vn.Offset + uint64(i)), "unexpected dynamic need version", vers} } cnt := f.ByteOrder.Uint16(d[i+2 : i+4]) fileoff := f.ByteOrder.Uint32(d[i+4 : i+8]) @@ -1632,14 +1680,14 @@ func (f *File) dynamicVersionNeeds(str []byte) bool { break } flags := DynamicVersionFlag(f.ByteOrder.Uint16(d[j+4 : j+6])) - other := f.ByteOrder.Uint16(d[j+6 : j+8]) + index := f.ByteOrder.Uint16(d[j+6 : j+8]) nameoff := f.ByteOrder.Uint32(d[j+8 : j+12]) next := f.ByteOrder.Uint32(d[j+12 : j+16]) depName, _ := getString(str, int(nameoff)) deps = append(deps, DynamicVersionDep{ Flags: flags, - Other: other, + Index: index, Dep: depName, }) @@ -1650,9 +1698,8 @@ func (f *File) dynamicVersionNeeds(str []byte) bool { } dynVerNeeds = append(dynVerNeeds, DynamicVersionNeed{ - Version: vers, - Name: file, - Needs: deps, + Name: file, + Needs: deps, }) if next == 0 { @@ -1663,7 +1710,7 @@ func (f *File) dynamicVersionNeeds(str []byte) bool { f.dynVerNeeds = dynVerNeeds - return true + return nil } // DynamicVersionNeeds returns version dependencies for a dynamic object. @@ -1673,7 +1720,11 @@ func (f *File) DynamicVersionNeeds() ([]DynamicVersionNeed, error) { if err != nil { return nil, err } - if !f.gnuVersionInit(str) { + hasVersions, err := f.gnuVersionInit(str) + if err != nil { + return nil, err + } + if !hasVersions { return nil, errors.New("DynamicVersionNeeds: missing version table") } } @@ -1683,66 +1734,59 @@ func (f *File) DynamicVersionNeeds() ([]DynamicVersionNeed, error) { // gnuVersionInit parses the GNU version tables // for use by calls to gnuVersion. -func (f *File) gnuVersionInit(str []byte) bool { +// It reports whether any version tables were found. +func (f *File) gnuVersionInit(str []byte) (bool, error) { // Versym parallels symbol table, indexing into verneed. vs := f.SectionByType(SHT_GNU_VERSYM) if vs == nil { - return false + return false, nil } d, _ := vs.Data() f.gnuVersym = d - f.dynamicVersions(str) - f.dynamicVersionNeeds(str) - return true + if err := f.dynamicVersions(str); err != nil { + return false, err + } + if err := f.dynamicVersionNeeds(str); err != nil { + return false, err + } + return true, nil } // gnuVersion adds Library and Version information to sym, // which came from offset i of the symbol table. -func (f *File) gnuVersion(i int) (versionIndex int16, version string, library string, versionFlags SymbolVersionFlag) { +func (f *File) gnuVersion(i int) (hasVersion bool, versionIndex VersionIndex, version string, library string) { // Each entry is two bytes; skip undef entry at beginning. i = (i + 1) * 2 if i >= len(f.gnuVersym) { - return -1, "", "", VerFlagNone + return false, 0, "", "" } s := f.gnuVersym[i:] if len(s) < 2 { - return -1, "", "", VerFlagNone + return false, 0, "", "" } - j := int32(f.ByteOrder.Uint16(s)) - var ndx = int16(j & 0x7fff) + vi := VersionIndex(f.ByteOrder.Uint16(s)) + ndx := vi.Index() - if ndx == 0 { - return ndx, "", "", VerFlagLocal - } else if ndx == 1 { - return ndx, "", "", VerFlagGlobal - } - - if ndx < 2 { - return 0, "", "", VerFlagNone + if ndx == 0 || ndx == 1 { + return true, vi, "", "" } for _, v := range f.dynVerNeeds { for _, n := range v.Needs { - if uint16(ndx) == n.Other { - return ndx, n.Dep, v.Name, VerFlagHidden + if ndx == n.Index { + return true, vi, n.Dep, v.Name } } } for _, v := range f.dynVers { - if uint16(ndx) == v.Index { - if len(v.Deps) > 0 { - flags := VerFlagNone - if j&0x8000 != 0 { - flags = VerFlagHidden - } - return ndx, v.Deps[0], "", flags - } + if ndx == v.Index { + return true, vi, v.Name, "" } } - return -1, "", "", VerFlagNone + return false, 0, "", "" } // ImportedLibraries returns the names of all libraries diff --git a/src/debug/elf/file_test.go b/src/debug/elf/file_test.go index 55d58b323405cd..1fdbbad04df44f 100644 --- a/src/debug/elf/file_test.go +++ b/src/debug/elf/file_test.go @@ -78,80 +78,80 @@ var fileTests = []fileTest{ }, []string{"libc.so.6"}, []Symbol{ - {"", 3, 0, -1, VerFlagNone, 1, 134512852, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 2, 134512876, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 3, 134513020, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 4, 134513292, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 5, 134513480, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 6, 134513512, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 7, 134513532, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 8, 134513612, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 9, 134513996, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 10, 134514008, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 11, 134518268, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 12, 134518280, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 13, 134518284, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 14, 134518436, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 15, 134518444, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 16, 134518452, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 17, 134518456, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 18, 134518484, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 19, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 20, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 21, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 22, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 23, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 24, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 25, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 26, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 27, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 28, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 29, 0, 0, "", ""}, - {"crt1.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"/usr/src/lib/csu/i386-elf/crti.S", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"/usr/src/lib/csu/i386-elf/crti.S", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"crtstuff.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"__CTOR_LIST__", 1, 0, -1, VerFlagNone, 14, 134518436, 0, "", ""}, - {"__DTOR_LIST__", 1, 0, -1, VerFlagNone, 15, 134518444, 0, "", ""}, - {"__EH_FRAME_BEGIN__", 1, 0, -1, VerFlagNone, 12, 134518280, 0, "", ""}, - {"__JCR_LIST__", 1, 0, -1, VerFlagNone, 16, 134518452, 0, "", ""}, - {"p.0", 1, 0, -1, VerFlagNone, 11, 134518276, 0, "", ""}, - {"completed.1", 1, 0, -1, VerFlagNone, 18, 134518484, 1, "", ""}, - {"__do_global_dtors_aux", 2, 0, -1, VerFlagNone, 8, 134513760, 0, "", ""}, - {"object.2", 1, 0, -1, VerFlagNone, 18, 134518488, 24, "", ""}, - {"frame_dummy", 2, 0, -1, VerFlagNone, 8, 134513836, 0, "", ""}, - {"crtstuff.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"__CTOR_END__", 1, 0, -1, VerFlagNone, 14, 134518440, 0, "", ""}, - {"__DTOR_END__", 1, 0, -1, VerFlagNone, 15, 134518448, 0, "", ""}, - {"__FRAME_END__", 1, 0, -1, VerFlagNone, 12, 134518280, 0, "", ""}, - {"__JCR_END__", 1, 0, -1, VerFlagNone, 16, 134518452, 0, "", ""}, - {"__do_global_ctors_aux", 2, 0, -1, VerFlagNone, 8, 134513960, 0, "", ""}, - {"/usr/src/lib/csu/i386-elf/crtn.S", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"/usr/src/lib/csu/i386-elf/crtn.S", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"hello.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"printf", 18, 0, -1, VerFlagNone, 0, 0, 44, "", ""}, - {"_DYNAMIC", 17, 0, -1, VerFlagNone, 65521, 134518284, 0, "", ""}, - {"__dso_handle", 17, 2, -1, VerFlagNone, 11, 134518272, 0, "", ""}, - {"_init", 18, 0, -1, VerFlagNone, 6, 134513512, 0, "", ""}, - {"environ", 17, 0, -1, VerFlagNone, 18, 134518512, 4, "", ""}, - {"__deregister_frame_info", 32, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, - {"__progname", 17, 0, -1, VerFlagNone, 11, 134518268, 4, "", ""}, - {"_start", 18, 0, -1, VerFlagNone, 8, 134513612, 145, "", ""}, - {"__bss_start", 16, 0, -1, VerFlagNone, 65521, 134518484, 0, "", ""}, - {"main", 18, 0, -1, VerFlagNone, 8, 134513912, 46, "", ""}, - {"_init_tls", 18, 0, -1, VerFlagNone, 0, 0, 5, "", ""}, - {"_fini", 18, 0, -1, VerFlagNone, 9, 134513996, 0, "", ""}, - {"atexit", 18, 0, -1, VerFlagNone, 0, 0, 43, "", ""}, - {"_edata", 16, 0, -1, VerFlagNone, 65521, 134518484, 0, "", ""}, - {"_GLOBAL_OFFSET_TABLE_", 17, 0, -1, VerFlagNone, 65521, 134518456, 0, "", ""}, - {"_end", 16, 0, -1, VerFlagNone, 65521, 134518516, 0, "", ""}, - {"exit", 18, 0, -1, VerFlagNone, 0, 0, 68, "", ""}, - {"_Jv_RegisterClasses", 32, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, - {"__register_frame_info", 32, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, + {"", 3, 0, false, 0, 1, 134512852, 0, "", ""}, + {"", 3, 0, false, 0, 2, 134512876, 0, "", ""}, + {"", 3, 0, false, 0, 3, 134513020, 0, "", ""}, + {"", 3, 0, false, 0, 4, 134513292, 0, "", ""}, + {"", 3, 0, false, 0, 5, 134513480, 0, "", ""}, + {"", 3, 0, false, 0, 6, 134513512, 0, "", ""}, + {"", 3, 0, false, 0, 7, 134513532, 0, "", ""}, + {"", 3, 0, false, 0, 8, 134513612, 0, "", ""}, + {"", 3, 0, false, 0, 9, 134513996, 0, "", ""}, + {"", 3, 0, false, 0, 10, 134514008, 0, "", ""}, + {"", 3, 0, false, 0, 11, 134518268, 0, "", ""}, + {"", 3, 0, false, 0, 12, 134518280, 0, "", ""}, + {"", 3, 0, false, 0, 13, 134518284, 0, "", ""}, + {"", 3, 0, false, 0, 14, 134518436, 0, "", ""}, + {"", 3, 0, false, 0, 15, 134518444, 0, "", ""}, + {"", 3, 0, false, 0, 16, 134518452, 0, "", ""}, + {"", 3, 0, false, 0, 17, 134518456, 0, "", ""}, + {"", 3, 0, false, 0, 18, 134518484, 0, "", ""}, + {"", 3, 0, false, 0, 19, 0, 0, "", ""}, + {"", 3, 0, false, 0, 20, 0, 0, "", ""}, + {"", 3, 0, false, 0, 21, 0, 0, "", ""}, + {"", 3, 0, false, 0, 22, 0, 0, "", ""}, + {"", 3, 0, false, 0, 23, 0, 0, "", ""}, + {"", 3, 0, false, 0, 24, 0, 0, "", ""}, + {"", 3, 0, false, 0, 25, 0, 0, "", ""}, + {"", 3, 0, false, 0, 26, 0, 0, "", ""}, + {"", 3, 0, false, 0, 27, 0, 0, "", ""}, + {"", 3, 0, false, 0, 28, 0, 0, "", ""}, + {"", 3, 0, false, 0, 29, 0, 0, "", ""}, + {"crt1.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"/usr/src/lib/csu/i386-elf/crti.S", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"/usr/src/lib/csu/i386-elf/crti.S", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"crtstuff.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"__CTOR_LIST__", 1, 0, false, 0, 14, 134518436, 0, "", ""}, + {"__DTOR_LIST__", 1, 0, false, 0, 15, 134518444, 0, "", ""}, + {"__EH_FRAME_BEGIN__", 1, 0, false, 0, 12, 134518280, 0, "", ""}, + {"__JCR_LIST__", 1, 0, false, 0, 16, 134518452, 0, "", ""}, + {"p.0", 1, 0, false, 0, 11, 134518276, 0, "", ""}, + {"completed.1", 1, 0, false, 0, 18, 134518484, 1, "", ""}, + {"__do_global_dtors_aux", 2, 0, false, 0, 8, 134513760, 0, "", ""}, + {"object.2", 1, 0, false, 0, 18, 134518488, 24, "", ""}, + {"frame_dummy", 2, 0, false, 0, 8, 134513836, 0, "", ""}, + {"crtstuff.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"__CTOR_END__", 1, 0, false, 0, 14, 134518440, 0, "", ""}, + {"__DTOR_END__", 1, 0, false, 0, 15, 134518448, 0, "", ""}, + {"__FRAME_END__", 1, 0, false, 0, 12, 134518280, 0, "", ""}, + {"__JCR_END__", 1, 0, false, 0, 16, 134518452, 0, "", ""}, + {"__do_global_ctors_aux", 2, 0, false, 0, 8, 134513960, 0, "", ""}, + {"/usr/src/lib/csu/i386-elf/crtn.S", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"/usr/src/lib/csu/i386-elf/crtn.S", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"hello.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"printf", 18, 0, false, 0, 0, 0, 44, "", ""}, + {"_DYNAMIC", 17, 0, false, 0, 65521, 134518284, 0, "", ""}, + {"__dso_handle", 17, 2, false, 0, 11, 134518272, 0, "", ""}, + {"_init", 18, 0, false, 0, 6, 134513512, 0, "", ""}, + {"environ", 17, 0, false, 0, 18, 134518512, 4, "", ""}, + {"__deregister_frame_info", 32, 0, false, 0, 0, 0, 0, "", ""}, + {"__progname", 17, 0, false, 0, 11, 134518268, 4, "", ""}, + {"_start", 18, 0, false, 0, 8, 134513612, 145, "", ""}, + {"__bss_start", 16, 0, false, 0, 65521, 134518484, 0, "", ""}, + {"main", 18, 0, false, 0, 8, 134513912, 46, "", ""}, + {"_init_tls", 18, 0, false, 0, 0, 0, 5, "", ""}, + {"_fini", 18, 0, false, 0, 9, 134513996, 0, "", ""}, + {"atexit", 18, 0, false, 0, 0, 0, 43, "", ""}, + {"_edata", 16, 0, false, 0, 65521, 134518484, 0, "", ""}, + {"_GLOBAL_OFFSET_TABLE_", 17, 0, false, 0, 65521, 134518456, 0, "", ""}, + {"_end", 16, 0, false, 0, 65521, 134518516, 0, "", ""}, + {"exit", 18, 0, false, 0, 0, 0, 68, "", ""}, + {"_Jv_RegisterClasses", 32, 0, false, 0, 0, 0, 0, "", ""}, + {"__register_frame_info", 32, 0, false, 0, 0, 0, 0, "", ""}, }, }, { @@ -208,79 +208,79 @@ var fileTests = []fileTest{ }, []string{"libc.so.6"}, []Symbol{ - {"", 3, 0, -1, VerFlagNone, 1, 4194816, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 2, 4194844, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 3, 4194880, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 4, 4194920, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 5, 4194952, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 6, 4195048, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 7, 4195110, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 8, 4195120, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 9, 4195152, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 10, 4195176, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 11, 4195224, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 12, 4195248, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 13, 4195296, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 14, 4195732, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 15, 4195748, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 16, 4195768, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 17, 4195808, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 18, 6293128, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 19, 6293144, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 20, 6293160, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 21, 6293168, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 22, 6293584, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 23, 6293592, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 24, 6293632, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 25, 6293656, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 26, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 27, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 28, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 29, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 30, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 31, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 32, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 33, 0, 0, "", ""}, - {"init.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"initfini.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"call_gmon_start", 2, 0, -1, VerFlagNone, 13, 4195340, 0, "", ""}, - {"crtstuff.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"__CTOR_LIST__", 1, 0, -1, VerFlagNone, 18, 6293128, 0, "", ""}, - {"__DTOR_LIST__", 1, 0, -1, VerFlagNone, 19, 6293144, 0, "", ""}, - {"__JCR_LIST__", 1, 0, -1, VerFlagNone, 20, 6293160, 0, "", ""}, - {"__do_global_dtors_aux", 2, 0, -1, VerFlagNone, 13, 4195376, 0, "", ""}, - {"completed.6183", 1, 0, -1, VerFlagNone, 25, 6293656, 1, "", ""}, - {"p.6181", 1, 0, -1, VerFlagNone, 24, 6293648, 0, "", ""}, - {"frame_dummy", 2, 0, -1, VerFlagNone, 13, 4195440, 0, "", ""}, - {"crtstuff.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"__CTOR_END__", 1, 0, -1, VerFlagNone, 18, 6293136, 0, "", ""}, - {"__DTOR_END__", 1, 0, -1, VerFlagNone, 19, 6293152, 0, "", ""}, - {"__FRAME_END__", 1, 0, -1, VerFlagNone, 17, 4195968, 0, "", ""}, - {"__JCR_END__", 1, 0, -1, VerFlagNone, 20, 6293160, 0, "", ""}, - {"__do_global_ctors_aux", 2, 0, -1, VerFlagNone, 13, 4195680, 0, "", ""}, - {"initfini.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"hello.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"_GLOBAL_OFFSET_TABLE_", 1, 2, -1, VerFlagNone, 23, 6293592, 0, "", ""}, - {"__init_array_end", 0, 2, -1, VerFlagNone, 18, 6293124, 0, "", ""}, - {"__init_array_start", 0, 2, -1, VerFlagNone, 18, 6293124, 0, "", ""}, - {"_DYNAMIC", 1, 2, -1, VerFlagNone, 21, 6293168, 0, "", ""}, - {"data_start", 32, 0, -1, VerFlagNone, 24, 6293632, 0, "", ""}, - {"__libc_csu_fini", 18, 0, -1, VerFlagNone, 13, 4195520, 2, "", ""}, - {"_start", 18, 0, -1, VerFlagNone, 13, 4195296, 0, "", ""}, - {"__gmon_start__", 32, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, - {"_Jv_RegisterClasses", 32, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, - {"puts@@GLIBC_2.2.5", 18, 0, -1, VerFlagNone, 0, 0, 396, "", ""}, - {"_fini", 18, 0, -1, VerFlagNone, 14, 4195732, 0, "", ""}, - {"__libc_start_main@@GLIBC_2.2.5", 18, 0, -1, VerFlagNone, 0, 0, 450, "", ""}, - {"_IO_stdin_used", 17, 0, -1, VerFlagNone, 15, 4195748, 4, "", ""}, - {"__data_start", 16, 0, -1, VerFlagNone, 24, 6293632, 0, "", ""}, - {"__dso_handle", 17, 2, -1, VerFlagNone, 24, 6293640, 0, "", ""}, - {"__libc_csu_init", 18, 0, -1, VerFlagNone, 13, 4195536, 137, "", ""}, - {"__bss_start", 16, 0, -1, VerFlagNone, 65521, 6293656, 0, "", ""}, - {"_end", 16, 0, -1, VerFlagNone, 65521, 6293664, 0, "", ""}, - {"_edata", 16, 0, -1, VerFlagNone, 65521, 6293656, 0, "", ""}, - {"main", 18, 0, -1, VerFlagNone, 13, 4195480, 27, "", ""}, - {"_init", 18, 0, -1, VerFlagNone, 11, 4195224, 0, "", ""}, + {"", 3, 0, false, 0, 1, 4194816, 0, "", ""}, + {"", 3, 0, false, 0, 2, 4194844, 0, "", ""}, + {"", 3, 0, false, 0, 3, 4194880, 0, "", ""}, + {"", 3, 0, false, 0, 4, 4194920, 0, "", ""}, + {"", 3, 0, false, 0, 5, 4194952, 0, "", ""}, + {"", 3, 0, false, 0, 6, 4195048, 0, "", ""}, + {"", 3, 0, false, 0, 7, 4195110, 0, "", ""}, + {"", 3, 0, false, 0, 8, 4195120, 0, "", ""}, + {"", 3, 0, false, 0, 9, 4195152, 0, "", ""}, + {"", 3, 0, false, 0, 10, 4195176, 0, "", ""}, + {"", 3, 0, false, 0, 11, 4195224, 0, "", ""}, + {"", 3, 0, false, 0, 12, 4195248, 0, "", ""}, + {"", 3, 0, false, 0, 13, 4195296, 0, "", ""}, + {"", 3, 0, false, 0, 14, 4195732, 0, "", ""}, + {"", 3, 0, false, 0, 15, 4195748, 0, "", ""}, + {"", 3, 0, false, 0, 16, 4195768, 0, "", ""}, + {"", 3, 0, false, 0, 17, 4195808, 0, "", ""}, + {"", 3, 0, false, 0, 18, 6293128, 0, "", ""}, + {"", 3, 0, false, 0, 19, 6293144, 0, "", ""}, + {"", 3, 0, false, 0, 20, 6293160, 0, "", ""}, + {"", 3, 0, false, 0, 21, 6293168, 0, "", ""}, + {"", 3, 0, false, 0, 22, 6293584, 0, "", ""}, + {"", 3, 0, false, 0, 23, 6293592, 0, "", ""}, + {"", 3, 0, false, 0, 24, 6293632, 0, "", ""}, + {"", 3, 0, false, 0, 25, 6293656, 0, "", ""}, + {"", 3, 0, false, 0, 26, 0, 0, "", ""}, + {"", 3, 0, false, 0, 27, 0, 0, "", ""}, + {"", 3, 0, false, 0, 28, 0, 0, "", ""}, + {"", 3, 0, false, 0, 29, 0, 0, "", ""}, + {"", 3, 0, false, 0, 30, 0, 0, "", ""}, + {"", 3, 0, false, 0, 31, 0, 0, "", ""}, + {"", 3, 0, false, 0, 32, 0, 0, "", ""}, + {"", 3, 0, false, 0, 33, 0, 0, "", ""}, + {"init.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"initfini.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"call_gmon_start", 2, 0, false, 0, 13, 4195340, 0, "", ""}, + {"crtstuff.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"__CTOR_LIST__", 1, 0, false, 0, 18, 6293128, 0, "", ""}, + {"__DTOR_LIST__", 1, 0, false, 0, 19, 6293144, 0, "", ""}, + {"__JCR_LIST__", 1, 0, false, 0, 20, 6293160, 0, "", ""}, + {"__do_global_dtors_aux", 2, 0, false, 0, 13, 4195376, 0, "", ""}, + {"completed.6183", 1, 0, false, 0, 25, 6293656, 1, "", ""}, + {"p.6181", 1, 0, false, 0, 24, 6293648, 0, "", ""}, + {"frame_dummy", 2, 0, false, 0, 13, 4195440, 0, "", ""}, + {"crtstuff.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"__CTOR_END__", 1, 0, false, 0, 18, 6293136, 0, "", ""}, + {"__DTOR_END__", 1, 0, false, 0, 19, 6293152, 0, "", ""}, + {"__FRAME_END__", 1, 0, false, 0, 17, 4195968, 0, "", ""}, + {"__JCR_END__", 1, 0, false, 0, 20, 6293160, 0, "", ""}, + {"__do_global_ctors_aux", 2, 0, false, 0, 13, 4195680, 0, "", ""}, + {"initfini.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"hello.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"_GLOBAL_OFFSET_TABLE_", 1, 2, false, 0, 23, 6293592, 0, "", ""}, + {"__init_array_end", 0, 2, false, 0, 18, 6293124, 0, "", ""}, + {"__init_array_start", 0, 2, false, 0, 18, 6293124, 0, "", ""}, + {"_DYNAMIC", 1, 2, false, 0, 21, 6293168, 0, "", ""}, + {"data_start", 32, 0, false, 0, 24, 6293632, 0, "", ""}, + {"__libc_csu_fini", 18, 0, false, 0, 13, 4195520, 2, "", ""}, + {"_start", 18, 0, false, 0, 13, 4195296, 0, "", ""}, + {"__gmon_start__", 32, 0, false, 0, 0, 0, 0, "", ""}, + {"_Jv_RegisterClasses", 32, 0, false, 0, 0, 0, 0, "", ""}, + {"puts@@GLIBC_2.2.5", 18, 0, false, 0, 0, 0, 396, "", ""}, + {"_fini", 18, 0, false, 0, 14, 4195732, 0, "", ""}, + {"__libc_start_main@@GLIBC_2.2.5", 18, 0, false, 0, 0, 0, 450, "", ""}, + {"_IO_stdin_used", 17, 0, false, 0, 15, 4195748, 4, "", ""}, + {"__data_start", 16, 0, false, 0, 24, 6293632, 0, "", ""}, + {"__dso_handle", 17, 2, false, 0, 24, 6293640, 0, "", ""}, + {"__libc_csu_init", 18, 0, false, 0, 13, 4195536, 137, "", ""}, + {"__bss_start", 16, 0, false, 0, 65521, 6293656, 0, "", ""}, + {"_end", 16, 0, false, 0, 65521, 6293664, 0, "", ""}, + {"_edata", 16, 0, false, 0, 65521, 6293656, 0, "", ""}, + {"main", 18, 0, false, 0, 13, 4195480, 27, "", ""}, + {"_init", 18, 0, false, 0, 11, 4195224, 0, "", ""}, }, }, { @@ -338,21 +338,21 @@ var fileTests = []fileTest{ []ProgHeader{}, nil, []Symbol{ - {"hello.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 1, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 3, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 4, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 5, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 6, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 8, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 9, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 11, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 13, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 15, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 16, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 14, 0, 0, "", ""}, - {"main", 18, 0, -1, VerFlagNone, 1, 0, 23, "", ""}, - {"puts", 16, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, + {"hello.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"", 3, 0, false, 0, 1, 0, 0, "", ""}, + {"", 3, 0, false, 0, 3, 0, 0, "", ""}, + {"", 3, 0, false, 0, 4, 0, 0, "", ""}, + {"", 3, 0, false, 0, 5, 0, 0, "", ""}, + {"", 3, 0, false, 0, 6, 0, 0, "", ""}, + {"", 3, 0, false, 0, 8, 0, 0, "", ""}, + {"", 3, 0, false, 0, 9, 0, 0, "", ""}, + {"", 3, 0, false, 0, 11, 0, 0, "", ""}, + {"", 3, 0, false, 0, 13, 0, 0, "", ""}, + {"", 3, 0, false, 0, 15, 0, 0, "", ""}, + {"", 3, 0, false, 0, 16, 0, 0, "", ""}, + {"", 3, 0, false, 0, 14, 0, 0, "", ""}, + {"main", 18, 0, false, 0, 1, 0, 23, "", ""}, + {"puts", 16, 0, false, 0, 0, 0, 0, "", ""}, }, }, { @@ -384,21 +384,21 @@ var fileTests = []fileTest{ []ProgHeader{}, nil, []Symbol{ - {"hello.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 1, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 3, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 4, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 5, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 6, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 8, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 9, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 11, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 13, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 15, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 16, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 14, 0, 0, "", ""}, - {"main", 18, 0, -1, VerFlagNone, 1, 0, 27, "", ""}, - {"puts", 16, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, + {"hello.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"", 3, 0, false, 0, 1, 0, 0, "", ""}, + {"", 3, 0, false, 0, 3, 0, 0, "", ""}, + {"", 3, 0, false, 0, 4, 0, 0, "", ""}, + {"", 3, 0, false, 0, 5, 0, 0, "", ""}, + {"", 3, 0, false, 0, 6, 0, 0, "", ""}, + {"", 3, 0, false, 0, 8, 0, 0, "", ""}, + {"", 3, 0, false, 0, 9, 0, 0, "", ""}, + {"", 3, 0, false, 0, 11, 0, 0, "", ""}, + {"", 3, 0, false, 0, 13, 0, 0, "", ""}, + {"", 3, 0, false, 0, 15, 0, 0, "", ""}, + {"", 3, 0, false, 0, 16, 0, 0, "", ""}, + {"", 3, 0, false, 0, 14, 0, 0, "", ""}, + {"main", 18, 0, false, 0, 1, 0, 27, "", ""}, + {"puts", 16, 0, false, 0, 0, 0, 0, "", ""}, }, }, { @@ -430,21 +430,21 @@ var fileTests = []fileTest{ []ProgHeader{}, nil, []Symbol{ - {"hello.c", 4, 0, -1, VerFlagNone, 65521, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 1, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 3, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 4, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 5, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 6, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 8, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 9, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 11, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 13, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 15, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 16, 0, 0, "", ""}, - {"", 3, 0, -1, VerFlagNone, 14, 0, 0, "", ""}, - {"main", 18, 0, -1, VerFlagNone, 1, 0, 44, "", ""}, - {"puts", 16, 0, -1, VerFlagNone, 0, 0, 0, "", ""}, + {"hello.c", 4, 0, false, 0, 65521, 0, 0, "", ""}, + {"", 3, 0, false, 0, 1, 0, 0, "", ""}, + {"", 3, 0, false, 0, 3, 0, 0, "", ""}, + {"", 3, 0, false, 0, 4, 0, 0, "", ""}, + {"", 3, 0, false, 0, 5, 0, 0, "", ""}, + {"", 3, 0, false, 0, 6, 0, 0, "", ""}, + {"", 3, 0, false, 0, 8, 0, 0, "", ""}, + {"", 3, 0, false, 0, 9, 0, 0, "", ""}, + {"", 3, 0, false, 0, 11, 0, 0, "", ""}, + {"", 3, 0, false, 0, 13, 0, 0, "", ""}, + {"", 3, 0, false, 0, 15, 0, 0, "", ""}, + {"", 3, 0, false, 0, 16, 0, 0, "", ""}, + {"", 3, 0, false, 0, 14, 0, 0, "", ""}, + {"main", 18, 0, false, 0, 1, 0, 44, "", ""}, + {"puts", 16, 0, false, 0, 0, 0, 0, "", ""}, }, }, } diff --git a/src/debug/elf/symbols_test.go b/src/debug/elf/symbols_test.go index ecc897b4a8aff6..6053d99acc1a0b 100644 --- a/src/debug/elf/symbols_test.go +++ b/src/debug/elf/symbols_test.go @@ -37,8 +37,21 @@ func TestSymbols(t *testing.T) { fs = []Symbol{} } if !reflect.DeepEqual(ts, fs) { - t.Errorf("%s: Symbols = %v, want %v", file, ts, fs) + t.Errorf("%s: Symbols = %v, want %v", file, fs, ts) } + + for i, s := range fs { + if s.HasVersion { + // No hidden versions here. + if s.VersionIndex.IsHidden() { + t.Errorf("%s: symbol %d: unexpected hidden version", file, i) + } + if got, want := s.VersionIndex.Index(), uint16(s.VersionIndex); got != want { + t.Errorf("%s: symbol %d: VersionIndex.Index() == %d, want %d", file, i, got, want) + } + } + } + } for file, ts := range symbolsGolden { do(file, ts, (*File).Symbols) @@ -56,8 +69,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1, Value: 0x400200, Size: 0x0, @@ -66,8 +79,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x2, Value: 0x40021C, Size: 0x0, @@ -76,8 +89,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x3, Value: 0x400240, Size: 0x0, @@ -86,8 +99,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x4, Value: 0x400268, Size: 0x0, @@ -96,8 +109,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x5, Value: 0x400288, Size: 0x0, @@ -106,8 +119,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x6, Value: 0x4002E8, Size: 0x0, @@ -116,8 +129,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x7, Value: 0x400326, Size: 0x0, @@ -126,8 +139,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x8, Value: 0x400330, Size: 0x0, @@ -136,8 +149,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x9, Value: 0x400350, Size: 0x0, @@ -146,8 +159,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xA, Value: 0x400368, Size: 0x0, @@ -156,8 +169,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xB, Value: 0x400398, Size: 0x0, @@ -166,8 +179,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xC, Value: 0x4003B0, Size: 0x0, @@ -176,8 +189,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x4003E0, Size: 0x0, @@ -186,8 +199,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xE, Value: 0x400594, Size: 0x0, @@ -196,8 +209,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xF, Value: 0x4005A4, Size: 0x0, @@ -206,8 +219,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x10, Value: 0x4005B8, Size: 0x0, @@ -216,8 +229,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x11, Value: 0x4005E0, Size: 0x0, @@ -226,8 +239,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x12, Value: 0x600688, Size: 0x0, @@ -236,8 +249,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x13, Value: 0x600698, Size: 0x0, @@ -246,8 +259,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x14, Value: 0x6006A8, Size: 0x0, @@ -256,8 +269,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x15, Value: 0x6006B0, Size: 0x0, @@ -266,8 +279,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x16, Value: 0x600850, Size: 0x0, @@ -276,8 +289,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x17, Value: 0x600858, Size: 0x0, @@ -286,8 +299,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x18, Value: 0x600880, Size: 0x0, @@ -296,8 +309,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x19, Value: 0x600898, Size: 0x0, @@ -306,8 +319,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1A, Value: 0x0, Size: 0x0, @@ -316,8 +329,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1B, Value: 0x0, Size: 0x0, @@ -326,8 +339,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1C, Value: 0x0, Size: 0x0, @@ -336,8 +349,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1D, Value: 0x0, Size: 0x0, @@ -346,8 +359,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1E, Value: 0x0, Size: 0x0, @@ -356,8 +369,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1F, Value: 0x0, Size: 0x0, @@ -366,8 +379,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x20, Value: 0x0, Size: 0x0, @@ -376,8 +389,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x21, Value: 0x0, Size: 0x0, @@ -386,8 +399,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "init.c", Info: 0x4, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -396,8 +409,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "initfini.c", Info: 0x4, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -406,8 +419,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "call_gmon_start", Info: 0x2, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x40040C, Size: 0x0, @@ -416,8 +429,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "crtstuff.c", Info: 0x4, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -426,8 +439,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__CTOR_LIST__", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x12, Value: 0x600688, Size: 0x0, @@ -436,8 +449,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__DTOR_LIST__", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x13, Value: 0x600698, Size: 0x0, @@ -446,8 +459,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__JCR_LIST__", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x14, Value: 0x6006A8, Size: 0x0, @@ -456,8 +469,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__do_global_dtors_aux", Info: 0x2, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x400430, Size: 0x0, @@ -466,8 +479,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "completed.6183", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x19, Value: 0x600898, Size: 0x1, @@ -476,8 +489,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "p.6181", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x18, Value: 0x600890, Size: 0x0, @@ -486,8 +499,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "frame_dummy", Info: 0x2, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x400470, Size: 0x0, @@ -496,8 +509,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "crtstuff.c", Info: 0x4, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -506,8 +519,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__CTOR_END__", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x12, Value: 0x600690, Size: 0x0, @@ -516,8 +529,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__DTOR_END__", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x13, Value: 0x6006A0, Size: 0x0, @@ -526,8 +539,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__FRAME_END__", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x11, Value: 0x400680, Size: 0x0, @@ -536,8 +549,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__JCR_END__", Info: 0x1, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x14, Value: 0x6006A8, Size: 0x0, @@ -546,8 +559,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__do_global_ctors_aux", Info: 0x2, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x400560, Size: 0x0, @@ -556,8 +569,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "initfini.c", Info: 0x4, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -566,8 +579,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "hello.c", Info: 0x4, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -576,8 +589,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_GLOBAL_OFFSET_TABLE_", Info: 0x1, Other: 0x2, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x17, Value: 0x600858, Size: 0x0, @@ -586,8 +599,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__init_array_end", Info: 0x0, Other: 0x2, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x12, Value: 0x600684, Size: 0x0, @@ -596,8 +609,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__init_array_start", Info: 0x0, Other: 0x2, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x12, Value: 0x600684, Size: 0x0, @@ -606,8 +619,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_DYNAMIC", Info: 0x1, Other: 0x2, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x15, Value: 0x6006B0, Size: 0x0, @@ -616,8 +629,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "data_start", Info: 0x20, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x18, Value: 0x600880, Size: 0x0, @@ -626,8 +639,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__libc_csu_fini", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x4004C0, Size: 0x2, @@ -636,8 +649,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_start", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x4003E0, Size: 0x0, @@ -646,8 +659,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__gmon_start__", Info: 0x20, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x0, Value: 0x0, Size: 0x0, @@ -656,8 +669,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_Jv_RegisterClasses", Info: 0x20, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x0, Value: 0x0, Size: 0x0, @@ -666,8 +679,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "puts@@GLIBC_2.2.5", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x0, Value: 0x0, Size: 0x18C, @@ -676,8 +689,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_fini", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xE, Value: 0x400594, Size: 0x0, @@ -686,8 +699,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__libc_start_main@@GLIBC_2.2.5", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x0, Value: 0x0, Size: 0x1C2, @@ -696,8 +709,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_IO_stdin_used", Info: 0x11, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xF, Value: 0x4005A4, Size: 0x4, @@ -706,8 +719,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__data_start", Info: 0x10, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x18, Value: 0x600880, Size: 0x0, @@ -716,8 +729,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__dso_handle", Info: 0x11, Other: 0x2, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x18, Value: 0x600888, Size: 0x0, @@ -726,8 +739,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__libc_csu_init", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x4004D0, Size: 0x89, @@ -736,8 +749,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "__bss_start", Info: 0x10, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x600898, Size: 0x0, @@ -746,8 +759,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_end", Info: 0x10, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x6008A0, Size: 0x0, @@ -756,8 +769,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_edata", Info: 0x10, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x600898, Size: 0x0, @@ -766,8 +779,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "main", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x400498, Size: 0x1B, @@ -776,8 +789,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "_init", Info: 0x12, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xB, Value: 0x400398, Size: 0x0, @@ -788,8 +801,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "go-relocation-test-clang.c", Info: 0x4, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -798,8 +811,8 @@ var symbolsGolden = map[string][]Symbol{ Name: ".Linfo_string0", Info: 0x0, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xC, Value: 0x0, Size: 0x0, @@ -808,8 +821,8 @@ var symbolsGolden = map[string][]Symbol{ Name: ".Linfo_string1", Info: 0x0, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xC, Value: 0x2C, Size: 0x0, @@ -818,8 +831,8 @@ var symbolsGolden = map[string][]Symbol{ Name: ".Linfo_string2", Info: 0x0, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xC, Value: 0x47, Size: 0x0, @@ -828,8 +841,8 @@ var symbolsGolden = map[string][]Symbol{ Name: ".Linfo_string3", Info: 0x0, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xC, Value: 0x4C, Size: 0x0, @@ -838,8 +851,8 @@ var symbolsGolden = map[string][]Symbol{ Name: ".Linfo_string4", Info: 0x0, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xC, Value: 0x4E, Size: 0x0, @@ -848,8 +861,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x1, Value: 0x0, Size: 0x0, @@ -858,8 +871,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x2, Value: 0x0, Size: 0x0, @@ -868,8 +881,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x3, Value: 0x0, Size: 0x0, @@ -878,8 +891,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x4, Value: 0x0, Size: 0x0, @@ -888,8 +901,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x6, Value: 0x0, Size: 0x0, @@ -898,8 +911,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x7, Value: 0x0, Size: 0x0, @@ -908,8 +921,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x8, Value: 0x0, Size: 0x0, @@ -918,8 +931,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xA, Value: 0x0, Size: 0x0, @@ -928,8 +941,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xC, Value: 0x0, Size: 0x0, @@ -938,8 +951,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xD, Value: 0x0, Size: 0x0, @@ -948,8 +961,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xE, Value: 0x0, Size: 0x0, @@ -958,8 +971,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xF, Value: 0x0, Size: 0x0, @@ -968,8 +981,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "", Info: 0x3, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0x10, Value: 0x0, Size: 0x0, @@ -978,8 +991,8 @@ var symbolsGolden = map[string][]Symbol{ Name: "v", Info: 0x11, Other: 0x0, - VersionIndex: -1, - VersionFlags: VerFlagNone, + HasVersion: false, + VersionIndex: 0, Section: 0xFFF2, Value: 0x4, Size: 0x4, @@ -994,8 +1007,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "__gmon_start__", Info: 0x20, Other: 0x0, + HasVersion: true, VersionIndex: 0x0, - VersionFlags: VerFlagLocal, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1004,8 +1017,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "puts", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x2, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x18C, @@ -1016,8 +1029,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "__libc_start_main", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x2, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x1C2, @@ -1032,8 +1045,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSo3putEc", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1044,8 +1057,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "strchr", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x4, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1056,8 +1069,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "__cxa_finalize", Info: 0x22, Other: 0x0, + HasVersion: true, VersionIndex: 0x4, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1068,8 +1081,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSo5tellpEv", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1080,8 +1093,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSo5seekpElSt12_Ios_Seekdir", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1092,8 +1105,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_Znwm", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1104,8 +1117,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZdlPvm", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x5, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1116,8 +1129,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "__stack_chk_fail", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x6, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1128,8 +1141,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x7, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1140,8 +1153,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSo5seekpESt4fposI11__mbstate_tE", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1152,8 +1165,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSi4readEPcl", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1164,8 +1177,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSi5seekgESt4fposI11__mbstate_tE", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1176,8 +1189,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSo5writeEPKcl", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1188,8 +1201,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSi5seekgElSt12_Ios_Seekdir", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1200,8 +1213,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZSt21ios_base_library_initv", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x8, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1212,8 +1225,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "TIFFClientOpen", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x9, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1224,8 +1237,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSt9basic_iosIcSt11char_traitsIcEE5clearESt12_Ios_Iostate", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1236,8 +1249,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ZNSi5tellgEv", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x3, - VersionFlags: VerFlagHidden, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1248,8 +1261,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ITM_deregisterTMCloneTable", Info: 0x20, Other: 0x0, + HasVersion: true, VersionIndex: 0x1, - VersionFlags: VerFlagGlobal, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1258,8 +1271,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "__gmon_start__", Info: 0x20, Other: 0x0, + HasVersion: true, VersionIndex: 0x1, - VersionFlags: VerFlagGlobal, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1268,8 +1281,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_ITM_registerTMCloneTable", Info: 0x20, Other: 0x0, + HasVersion: true, VersionIndex: 0x1, - VersionFlags: VerFlagGlobal, Section: 0x0, Value: 0x0, Size: 0x0, @@ -1278,8 +1291,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "LIBTIFFXX_4.0", Info: 0x11, Other: 0x0, + HasVersion: true, VersionIndex: 0x2, - VersionFlags: VerFlagNone, Section: 0xFFF1, Value: 0x0, Size: 0x0, @@ -1290,8 +1303,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_Z14TIFFStreamOpenPKcPSo", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x2, - VersionFlags: VerFlagNone, Section: 0xF, Value: 0x1860, Size: 0xB8, @@ -1302,8 +1315,8 @@ var dynamicSymbolsGolden = map[string][]Symbol{ Name: "_Z14TIFFStreamOpenPKcPSi", Info: 0x12, Other: 0x0, + HasVersion: true, VersionIndex: 0x2, - VersionFlags: VerFlagNone, Section: 0xF, Value: 0x1920, Size: 0x13, diff --git a/src/encoding/base64/base64.go b/src/encoding/base64/base64.go index f94bea132cbde3..57aa1a697fc7bd 100644 --- a/src/encoding/base64/base64.go +++ b/src/encoding/base64/base64.go @@ -6,7 +6,7 @@ package base64 import ( - "encoding/binary" + "internal/byteorder" "io" "slices" "strconv" @@ -538,7 +538,7 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { enc.decodeMap[src2[6]], enc.decodeMap[src2[7]], ); ok { - binary.BigEndian.PutUint64(dst[n:], dn) + byteorder.BEPutUint64(dst[n:], dn) n += 6 si += 8 } else { @@ -559,7 +559,7 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { enc.decodeMap[src2[2]], enc.decodeMap[src2[3]], ); ok { - binary.BigEndian.PutUint32(dst[n:], dn) + byteorder.BEPutUint32(dst[n:], dn) n += 3 si += 4 } else { diff --git a/src/encoding/binary/binary.go b/src/encoding/binary/binary.go index d80aa8e11a5638..c92dfded272798 100644 --- a/src/encoding/binary/binary.go +++ b/src/encoding/binary/binary.go @@ -65,17 +65,20 @@ var BigEndian bigEndian type littleEndian struct{} +// Uint16 returns the uint16 representation of b[0:2]. func (littleEndian) Uint16(b []byte) uint16 { _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 return uint16(b[0]) | uint16(b[1])<<8 } +// PutUint16 stores v into b[0:2]. func (littleEndian) PutUint16(b []byte, v uint16) { _ = b[1] // early bounds check to guarantee safety of writes below b[0] = byte(v) b[1] = byte(v >> 8) } +// AppendUint16 appends the bytes of v to b and returns the appended slice. func (littleEndian) AppendUint16(b []byte, v uint16) []byte { return append(b, byte(v), @@ -83,11 +86,13 @@ func (littleEndian) AppendUint16(b []byte, v uint16) []byte { ) } +// Uint32 returns the uint32 representation of b[0:4]. func (littleEndian) Uint32(b []byte) uint32 { _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 } +// PutUint32 stores v into b[0:4]. func (littleEndian) PutUint32(b []byte, v uint32) { _ = b[3] // early bounds check to guarantee safety of writes below b[0] = byte(v) @@ -96,6 +101,7 @@ func (littleEndian) PutUint32(b []byte, v uint32) { b[3] = byte(v >> 24) } +// AppendUint32 appends the bytes of v to b and returns the appended slice. func (littleEndian) AppendUint32(b []byte, v uint32) []byte { return append(b, byte(v), @@ -105,12 +111,14 @@ func (littleEndian) AppendUint32(b []byte, v uint32) []byte { ) } +// Uint64 returns the uint64 representation of b[0:8]. func (littleEndian) Uint64(b []byte) uint64 { _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 } +// PutUint64 stores v into b[0:8]. func (littleEndian) PutUint64(b []byte, v uint64) { _ = b[7] // early bounds check to guarantee safety of writes below b[0] = byte(v) @@ -123,6 +131,7 @@ func (littleEndian) PutUint64(b []byte, v uint64) { b[7] = byte(v >> 56) } +// AppendUint64 appends the bytes of v to b and returns the appended slice. func (littleEndian) AppendUint64(b []byte, v uint64) []byte { return append(b, byte(v), @@ -142,17 +151,20 @@ func (littleEndian) GoString() string { return "binary.LittleEndian" } type bigEndian struct{} +// Uint16 returns the uint16 representation of b[0:2]. func (bigEndian) Uint16(b []byte) uint16 { _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 return uint16(b[1]) | uint16(b[0])<<8 } +// PutUint16 stores v into b[0:2]. func (bigEndian) PutUint16(b []byte, v uint16) { _ = b[1] // early bounds check to guarantee safety of writes below b[0] = byte(v >> 8) b[1] = byte(v) } +// AppendUint16 appends the bytes of v to b and returns the appended slice. func (bigEndian) AppendUint16(b []byte, v uint16) []byte { return append(b, byte(v>>8), @@ -160,11 +172,13 @@ func (bigEndian) AppendUint16(b []byte, v uint16) []byte { ) } +// Uint32 returns the uint32 representation of b[0:4]. func (bigEndian) Uint32(b []byte) uint32 { _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 } +// PutUint32 stores v into b[0:4]. func (bigEndian) PutUint32(b []byte, v uint32) { _ = b[3] // early bounds check to guarantee safety of writes below b[0] = byte(v >> 24) @@ -173,6 +187,7 @@ func (bigEndian) PutUint32(b []byte, v uint32) { b[3] = byte(v) } +// AppendUint32 appends the bytes of v to b and returns the appended slice. func (bigEndian) AppendUint32(b []byte, v uint32) []byte { return append(b, byte(v>>24), @@ -182,12 +197,14 @@ func (bigEndian) AppendUint32(b []byte, v uint32) []byte { ) } +// Uint64 returns the uint64 representation of b[0:8]. func (bigEndian) Uint64(b []byte) uint64 { _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 } +// PutUint64 stores v into b[0:8]. func (bigEndian) PutUint64(b []byte, v uint64) { _ = b[7] // early bounds check to guarantee safety of writes below b[0] = byte(v >> 56) @@ -200,6 +217,7 @@ func (bigEndian) PutUint64(b []byte, v uint64) { b[7] = byte(v) } +// AppendUint64 appends the bytes of v to b and returns the appended slice. func (bigEndian) AppendUint64(b []byte, v uint64) []byte { return append(b, byte(v>>56), diff --git a/src/encoding/gob/decoder.go b/src/encoding/gob/decoder.go index eae307838e201e..c35398d1054e9e 100644 --- a/src/encoding/gob/decoder.go +++ b/src/encoding/gob/decoder.go @@ -199,7 +199,7 @@ func (dec *Decoder) Decode(e any) error { value := reflect.ValueOf(e) // If e represents a value as opposed to a pointer, the answer won't // get back to the caller. Make sure it's a pointer. - if value.Type().Kind() != reflect.Pointer { + if value.Kind() != reflect.Pointer { dec.err = errors.New("gob: attempt to decode into a non-pointer") return dec.err } diff --git a/src/encoding/gob/encode.go b/src/encoding/gob/encode.go index 5f4d2539faafe3..ed3494218ce70e 100644 --- a/src/encoding/gob/encode.go +++ b/src/encoding/gob/encode.go @@ -662,7 +662,7 @@ func (enc *Encoder) encode(b *encBuffer, value reflect.Value, ut *userTypeInfo) for i := 0; i < indir; i++ { value = reflect.Indirect(value) } - if ut.externalEnc == 0 && value.Type().Kind() == reflect.Struct { + if ut.externalEnc == 0 && value.Kind() == reflect.Struct { enc.encodeStruct(b, engine, value) } else { enc.encodeSingle(b, engine, value) diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 98102291ab8036..3b398c9fc323f5 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -113,9 +113,6 @@ func Unmarshal(data []byte, v any) error { // The input can be assumed to be a valid encoding of // a JSON value. UnmarshalJSON must copy the JSON data // if it wishes to retain the data after returning. -// -// By convention, to approximate the behavior of [Unmarshal] itself, -// Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op. type Unmarshaler interface { UnmarshalJSON([]byte) error } diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index de09fae50f09d1..8aad11b8bfbce2 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -458,10 +458,10 @@ var unmarshalTests = []struct { // Z has a "-" tag. {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}}, - {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true}, + {CaseName: Name(""), in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}, err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true}, {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}}, - {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true}, + {CaseName: Name(""), in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}, err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true}, {CaseName: Name(""), in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}}, {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}}, {CaseName: Name(""), in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true}, @@ -471,7 +471,7 @@ var unmarshalTests = []struct { {CaseName: Name(""), in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}}, {CaseName: Name(""), in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true}, {CaseName: Name(""), in: `[2, 3`, err: &SyntaxError{msg: "unexpected end of JSON input", Offset: 5}}, - {CaseName: Name(""), in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}}, + {CaseName: Name(""), in: `{"F3": -}`, ptr: new(V), err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}}, // raw value errors {CaseName: Name(""), in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}}, @@ -563,6 +563,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"2":4}`, ptr: new(map[u8marshal]int), + out: map[u8marshal]int{}, err: errMissingU8Prefix, }, @@ -571,36 +572,42 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"abc":"abc"}`, ptr: new(map[int]string), + out: map[int]string{}, err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeFor[int](), Offset: 2}, }, { CaseName: Name(""), in: `{"256":"abc"}`, ptr: new(map[uint8]string), + out: map[uint8]string{}, err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeFor[uint8](), Offset: 2}, }, { CaseName: Name(""), in: `{"128":"abc"}`, ptr: new(map[int8]string), + out: map[int8]string{}, err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeFor[int8](), Offset: 2}, }, { CaseName: Name(""), in: `{"-1":"abc"}`, ptr: new(map[uint8]string), + out: map[uint8]string{}, err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeFor[uint8](), Offset: 2}, }, { CaseName: Name(""), in: `{"F":{"a":2,"3":4}}`, ptr: new(map[string]map[int]int), + out: map[string]map[int]int{"F": {3: 4}}, err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[int](), Offset: 7}, }, { CaseName: Name(""), in: `{"F":{"a":2,"3":4}}`, ptr: new(map[string]map[uint]int), + out: map[string]map[uint]int{"F": {3: 4}}, err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeFor[uint](), Offset: 7}, }, @@ -682,6 +689,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"X": 1,"Y":2}`, ptr: new(S5), + out: S5{S8: S8{S9{Y: 2}}}, err: fmt.Errorf("json: unknown field \"X\""), disallowUnknownFields: true, }, @@ -695,6 +703,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"X": 1,"Y":2}`, ptr: new(S10), + out: S10{S13: S13{S8{S9{Y: 2}}}}, err: fmt.Errorf("json: unknown field \"X\""), disallowUnknownFields: true, }, @@ -889,6 +898,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"V": {"F4": {}, "F2": "hello"}}`, ptr: new(VOuter), + out: VOuter{V: V{F4: &VOuter{}}}, err: &UnmarshalTypeError{ Value: "string", Struct: "V", @@ -902,6 +912,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"Level1a": "hello"}`, ptr: new(Top), + out: Top{Embed0a: &Embed0a{}}, err: &UnmarshalTypeError{ Value: "string", Struct: "Top", @@ -947,7 +958,29 @@ var unmarshalTests = []struct { "Q": 18, "extra": true }`, - ptr: new(Top), + ptr: new(Top), + out: Top{ + Level0: 1, + Embed0: Embed0{ + Level1b: 2, + Level1c: 3, + }, + Embed0a: &Embed0a{Level1a: 5, Level1b: 6}, + Embed0b: &Embed0b{Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, Level1e: 12}, + Loop: Loop{ + Loop1: 13, + Loop2: 14, + Loop: nil, + }, + Embed0p: Embed0p{ + Point: image.Point{ + X: 15, + Y: 16, + }, + }, + Embed0q: Embed0q{Point: Point{Z: 17}}, + embed: embed{Q: 18}, + }, err: fmt.Errorf("json: unknown field \"extra\""), disallowUnknownFields: true, }, @@ -975,7 +1008,29 @@ var unmarshalTests = []struct { "Z": 17, "Q": 18 }`, - ptr: new(Top), + ptr: new(Top), + out: Top{ + Level0: 1, + Embed0: Embed0{ + Level1b: 2, + Level1c: 3, + }, + Embed0a: &Embed0a{Level1a: 5, Level1b: 6}, + Embed0b: &Embed0b{Level1a: 8, Level1b: 9, Level1c: 10, Level1d: 11, Level1e: 12}, + Loop: Loop{ + Loop1: 13, + Loop2: 14, + Loop: nil, + }, + Embed0p: Embed0p{ + Point: image.Point{ + X: 15, + Y: 16, + }, + }, + Embed0q: Embed0q{Point: Point{Z: 17}}, + embed: embed{Q: 18}, + }, err: fmt.Errorf("json: unknown field \"extra\""), disallowUnknownFields: true, }, @@ -985,12 +1040,14 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"data":{"test1": "bob", "test2": 123}}`, ptr: new(mapStringToStringData), + out: mapStringToStringData{map[string]string{"test1": "bob", "test2": ""}}, err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 37, Struct: "mapStringToStringData", Field: "data"}, }, { CaseName: Name(""), in: `{"data":{"test1": 123, "test2": "bob"}}`, ptr: new(mapStringToStringData), + out: mapStringToStringData{Data: map[string]string{"test1": "", "test2": "bob"}}, err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[string](), Offset: 21, Struct: "mapStringToStringData", Field: "data"}, }, @@ -1024,6 +1081,7 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`, ptr: new(PP), + out: PP{Ts: []T{{Y: 1}, {Y: 2}, {Y: 0}}}, err: &UnmarshalTypeError{ Value: "string", Struct: "T", @@ -1066,8 +1124,69 @@ var unmarshalTests = []struct { CaseName: Name(""), in: `{"A":"invalid"}`, ptr: new(map[string]Number), + out: map[string]Number{}, err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`), }, + + { + CaseName: Name(""), + in: `5`, + ptr: new(Number), + out: Number("5"), + }, + { + CaseName: Name(""), + in: `"5"`, + ptr: new(Number), + out: Number("5"), + }, + { + CaseName: Name(""), + in: `{"N":5}`, + ptr: new(struct{ N Number }), + out: struct{ N Number }{"5"}, + }, + { + CaseName: Name(""), + in: `{"N":"5"}`, + ptr: new(struct{ N Number }), + out: struct{ N Number }{"5"}, + }, + { + CaseName: Name(""), + in: `{"N":5}`, + ptr: new(struct { + N Number `json:",string"` + }), + err: fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal unquoted value into json.Number"), + }, + { + CaseName: Name(""), + in: `{"N":"5"}`, + ptr: new(struct { + N Number `json:",string"` + }), + out: struct { + N Number `json:",string"` + }{"5"}, + }, + + // Verify that syntactic errors are immediately fatal, + // while semantic errors are lazily reported + // (i.e., allow processing to continue). + { + CaseName: Name(""), + in: `[1,2,true,4,5}`, + ptr: new([]int), + err: &SyntaxError{msg: "invalid character '}' after array element", Offset: 14}, + }, + { + CaseName: Name(""), + in: `[1,2,true,4,5]`, + ptr: new([]int), + out: []int{1, 2, 0, 4, 5}, + err: &UnmarshalTypeError{Value: "bool", Type: reflect.TypeFor[int](), Offset: 9}, + }, } func TestMarshal(t *testing.T) { @@ -1202,7 +1321,7 @@ func TestUnmarshal(t *testing.T) { var scan scanner if err := checkValid(in, &scan); err != nil { if !equalError(err, tt.err) { - t.Fatalf("%s: checkValid error: %#v", tt.Where, err) + t.Fatalf("%s: checkValid error:\n\tgot %#v\n\twant %#v", tt.Where, err, tt.err) } } if tt.ptr == nil { @@ -1236,9 +1355,11 @@ func TestUnmarshal(t *testing.T) { dec.DisallowUnknownFields() } if err := dec.Decode(v.Interface()); !equalError(err, tt.err) { - t.Fatalf("%s: Decode error:\n\tgot: %#v\n\twant: %#v", tt.Where, err, tt.err) - } else if err != nil { - return + t.Fatalf("%s: Decode error:\n\tgot: %v\n\twant: %v\n\n\tgot: %#v\n\twant: %#v", tt.Where, err, tt.err, err, tt.err) + } else if err != nil && tt.out == nil { + // Initialize tt.out during an error where there are no mutations, + // so the output is just the zero value of the input type. + tt.out = reflect.Zero(v.Elem().Type()).Interface() } if got := v.Elem().Interface(); !reflect.DeepEqual(got, tt.out) { gotJSON, _ := Marshal(got) @@ -1797,19 +1918,12 @@ func TestNullString(t *testing.T) { } } -func intp(x int) *int { - p := new(int) - *p = x - return p -} - -func intpp(x *int) **int { - pp := new(*int) - *pp = x - return pp +func addr[T any](v T) *T { + return &v } func TestInterfaceSet(t *testing.T) { + errUnmarshal := &UnmarshalTypeError{Value: "object", Offset: 6, Type: reflect.TypeFor[int](), Field: "X"} tests := []struct { CaseName pre any @@ -1820,21 +1934,55 @@ func TestInterfaceSet(t *testing.T) { {Name(""), "foo", `2`, 2.0}, {Name(""), "foo", `true`, true}, {Name(""), "foo", `null`, nil}, - - {Name(""), nil, `null`, nil}, - {Name(""), new(int), `null`, nil}, - {Name(""), (*int)(nil), `null`, nil}, - {Name(""), new(*int), `null`, new(*int)}, - {Name(""), (**int)(nil), `null`, nil}, - {Name(""), intp(1), `null`, nil}, - {Name(""), intpp(nil), `null`, intpp(nil)}, - {Name(""), intpp(intp(1)), `null`, intpp(nil)}, + {Name(""), map[string]any{}, `true`, true}, + {Name(""), []string{}, `true`, true}, + + {Name(""), any(nil), `null`, any(nil)}, + {Name(""), (*int)(nil), `null`, any(nil)}, + {Name(""), (*int)(addr(0)), `null`, any(nil)}, + {Name(""), (*int)(addr(1)), `null`, any(nil)}, + {Name(""), (**int)(nil), `null`, any(nil)}, + {Name(""), (**int)(addr[*int](nil)), `null`, (**int)(addr[*int](nil))}, + {Name(""), (**int)(addr(addr(1))), `null`, (**int)(addr[*int](nil))}, + {Name(""), (***int)(nil), `null`, any(nil)}, + {Name(""), (***int)(addr[**int](nil)), `null`, (***int)(addr[**int](nil))}, + {Name(""), (***int)(addr(addr[*int](nil))), `null`, (***int)(addr[**int](nil))}, + {Name(""), (***int)(addr(addr(addr(1)))), `null`, (***int)(addr[**int](nil))}, + + {Name(""), any(nil), `2`, float64(2)}, + {Name(""), (int)(1), `2`, float64(2)}, + {Name(""), (*int)(nil), `2`, float64(2)}, + {Name(""), (*int)(addr(0)), `2`, (*int)(addr(2))}, + {Name(""), (*int)(addr(1)), `2`, (*int)(addr(2))}, + {Name(""), (**int)(nil), `2`, float64(2)}, + {Name(""), (**int)(addr[*int](nil)), `2`, (**int)(addr(addr(2)))}, + {Name(""), (**int)(addr(addr(1))), `2`, (**int)(addr(addr(2)))}, + {Name(""), (***int)(nil), `2`, float64(2)}, + {Name(""), (***int)(addr[**int](nil)), `2`, (***int)(addr(addr(addr(2))))}, + {Name(""), (***int)(addr(addr[*int](nil))), `2`, (***int)(addr(addr(addr(2))))}, + {Name(""), (***int)(addr(addr(addr(1)))), `2`, (***int)(addr(addr(addr(2))))}, + + {Name(""), any(nil), `{}`, map[string]any{}}, + {Name(""), (int)(1), `{}`, map[string]any{}}, + {Name(""), (*int)(nil), `{}`, map[string]any{}}, + {Name(""), (*int)(addr(0)), `{}`, errUnmarshal}, + {Name(""), (*int)(addr(1)), `{}`, errUnmarshal}, + {Name(""), (**int)(nil), `{}`, map[string]any{}}, + {Name(""), (**int)(addr[*int](nil)), `{}`, errUnmarshal}, + {Name(""), (**int)(addr(addr(1))), `{}`, errUnmarshal}, + {Name(""), (***int)(nil), `{}`, map[string]any{}}, + {Name(""), (***int)(addr[**int](nil)), `{}`, errUnmarshal}, + {Name(""), (***int)(addr(addr[*int](nil))), `{}`, errUnmarshal}, + {Name(""), (***int)(addr(addr(addr(1)))), `{}`, errUnmarshal}, } for _, tt := range tests { t.Run(tt.Name, func(t *testing.T) { b := struct{ X any }{tt.pre} blob := `{"X":` + tt.json + `}` if err := Unmarshal([]byte(blob), &b); err != nil { + if wantErr, _ := tt.post.(error); equalError(err, wantErr) { + return + } t.Fatalf("%s: Unmarshal(%#q) error: %v", tt.Where, blob, err) } if !reflect.DeepEqual(b.X, tt.post) { diff --git a/src/encoding/json/stream_test.go b/src/encoding/json/stream_test.go index 32ede8cc7e6271..46f9407c881c36 100644 --- a/src/encoding/json/stream_test.go +++ b/src/encoding/json/stream_test.go @@ -79,9 +79,9 @@ func TestEncoder(t *testing.T) { t.Fatalf("#%d.%d Encode error: %v", i, j, err) } } - if have, want := buf.String(), nlines(streamEncoded, i); have != want { + if got, want := buf.String(), nlines(streamEncoded, i); got != want { t.Errorf("encoding %d items: mismatch:", i) - diff(t, []byte(have), []byte(want)) + diff(t, []byte(got), []byte(want)) break } } @@ -148,9 +148,9 @@ func TestEncoderIndent(t *testing.T) { for _, v := range streamTest { enc.Encode(v) } - if have, want := buf.String(), streamEncodedIndent; have != want { - t.Error("Encode mismatch:") - diff(t, []byte(have), []byte(want)) + if got, want := buf.String(), streamEncodedIndent; got != want { + t.Errorf("Encode mismatch:\ngot:\n%s\n\nwant:\n%s", got, want) + diff(t, []byte(got), []byte(want)) } } diff --git a/src/encoding/json/tags_test.go b/src/encoding/json/tags_test.go index 1d2323dcee6014..eb43ff553095c0 100644 --- a/src/encoding/json/tags_test.go +++ b/src/encoding/json/tags_test.go @@ -4,9 +4,7 @@ package json -import ( - "testing" -) +import "testing" func TestTagParsing(t *testing.T) { name, opts := parseTag("field,foobar,foo") diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go index 3cc4968c762f3b..af25c20f0618dc 100644 --- a/src/encoding/xml/read.go +++ b/src/encoding/xml/read.go @@ -280,7 +280,7 @@ func (d *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error { } } - if val.Type().Kind() == reflect.Slice && val.Type().Elem().Kind() != reflect.Uint8 { + if val.Kind() == reflect.Slice && val.Type().Elem().Kind() != reflect.Uint8 { // Slice of element values. // Grow slice. n := val.Len() diff --git a/src/encoding/xml/xml_test.go b/src/encoding/xml/xml_test.go index fc3c15eff1b689..10cefa068fec87 100644 --- a/src/encoding/xml/xml_test.go +++ b/src/encoding/xml/xml_test.go @@ -640,11 +640,11 @@ func TestIssue68387(t *testing.T) { if tok3, err = dec.RawToken(); err != io.EOF || tok3 != nil { t.Fatalf("Missed EOF") } - s := StartElement{Name{"", "item"}, []Attr{Attr{Name{"","b"}, "]]>"}}} + s := StartElement{Name{"", "item"}, []Attr{Attr{Name{"", "b"}, "]]>"}}} if !reflect.DeepEqual(tok1.(StartElement), s) { t.Error("Wrong start element") } - e := EndElement{Name{"","item"}} + e := EndElement{Name{"", "item"}} if tok2.(EndElement) != e { t.Error("Wrong end element") } diff --git a/src/fmt/doc.go b/src/fmt/doc.go index b90db7bedc684b..fa0ffa7f00ccc7 100644 --- a/src/fmt/doc.go +++ b/src/fmt/doc.go @@ -50,6 +50,9 @@ Floating-point and complex constituents: %x hexadecimal notation (with decimal power of two exponent), e.g. -0x1.23abcp+20 %X upper-case hexadecimal notation, e.g. -0X1.23ABCP+20 + The exponent is always a decimal integer. + For formats other than %b the exponent is at least two digits. + String and slice of bytes (treated equivalently with these verbs): %s the uninterpreted bytes of the string or slice diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index b7f9ccd494d099..82daf627717814 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -11,7 +11,6 @@ import ( "io" "math" "reflect" - "runtime" "strings" "testing" "time" @@ -112,6 +111,19 @@ func (p *P) String() string { return "String(p)" } +// Fn is a function type with a String method. +type Fn func() int + +func (fn Fn) String() string { return "String(fn)" } + +var fnValue Fn + +// U is a type with two unexported function fields. +type U struct { + u func() string + fn Fn +} + var barray = [5]renamedUint8{1, 2, 3, 4, 5} var bslice = barray[:] @@ -714,7 +726,6 @@ var fmtTests = []struct { // go syntax {"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`}, {"%#v", new(byte), "(*uint8)(0xPTR)"}, - {"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"}, {"%#v", make(chan int), "(chan int)(0xPTR)"}, {"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"}, {"%#v", 1000000000, "1000000000"}, @@ -737,6 +748,54 @@ var fmtTests = []struct { {"%#v", 1.2345678, "1.2345678"}, {"%#v", float32(1.2345678), "1.2345678"}, + // functions + {"%v", TestFmtInterface, "0xPTR"}, // simple function + {"%v", reflect.ValueOf(TestFmtInterface), "0xPTR"}, + {"%v", G.GoString, "0xPTR"}, // method expression + {"%v", reflect.ValueOf(G.GoString), "0xPTR"}, + {"%v", G(23).GoString, "0xPTR"}, // method value + {"%v", reflect.ValueOf(G(23).GoString), "0xPTR"}, + {"%v", reflect.ValueOf(G(23)).Method(0), "0xPTR"}, + {"%v", Fn.String, "0xPTR"}, // method of function type + {"%v", reflect.ValueOf(Fn.String), "0xPTR"}, + {"%v", fnValue, "String(fn)"}, // variable of function type with String method + {"%v", reflect.ValueOf(fnValue), "String(fn)"}, + {"%v", [1]Fn{fnValue}, "[String(fn)]"}, // array of function type with String method + {"%v", reflect.ValueOf([1]Fn{fnValue}), "[String(fn)]"}, + {"%v", fnValue.String, "0xPTR"}, // method value from function type + {"%v", reflect.ValueOf(fnValue.String), "0xPTR"}, + {"%v", reflect.ValueOf(fnValue).Method(0), "0xPTR"}, + {"%v", U{}.u, ""}, // unexported function field + {"%v", reflect.ValueOf(U{}.u), ""}, + {"%v", reflect.ValueOf(U{}).Field(0), ""}, + {"%v", U{fn: fnValue}.fn, "String(fn)"}, // unexported field of function type with String method + {"%v", reflect.ValueOf(U{fn: fnValue}.fn), "String(fn)"}, + {"%v", reflect.ValueOf(U{fn: fnValue}).Field(1), ""}, + + // functions with go syntax + {"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"}, // simple function + {"%#v", reflect.ValueOf(TestFmtInterface), "(func(*testing.T))(0xPTR)"}, + {"%#v", G.GoString, "(func(fmt_test.G) string)(0xPTR)"}, // method expression + {"%#v", reflect.ValueOf(G.GoString), "(func(fmt_test.G) string)(0xPTR)"}, + {"%#v", G(23).GoString, "(func() string)(0xPTR)"}, // method value + {"%#v", reflect.ValueOf(G(23).GoString), "(func() string)(0xPTR)"}, + {"%#v", reflect.ValueOf(G(23)).Method(0), "(func() string)(0xPTR)"}, + {"%#v", Fn.String, "(func(fmt_test.Fn) string)(0xPTR)"}, // method of function type + {"%#v", reflect.ValueOf(Fn.String), "(func(fmt_test.Fn) string)(0xPTR)"}, + {"%#v", fnValue, "(fmt_test.Fn)(nil)"}, // variable of function type with String method + {"%#v", reflect.ValueOf(fnValue), "(fmt_test.Fn)(nil)"}, + {"%#v", [1]Fn{fnValue}, "[1]fmt_test.Fn{(fmt_test.Fn)(nil)}"}, // array of function type with String method + {"%#v", reflect.ValueOf([1]Fn{fnValue}), "[1]fmt_test.Fn{(fmt_test.Fn)(nil)}"}, + {"%#v", fnValue.String, "(func() string)(0xPTR)"}, // method value from function type + {"%#v", reflect.ValueOf(fnValue.String), "(func() string)(0xPTR)"}, + {"%#v", reflect.ValueOf(fnValue).Method(0), "(func() string)(0xPTR)"}, + {"%#v", U{}.u, "(func() string)(nil)"}, // unexported function field + {"%#v", reflect.ValueOf(U{}.u), "(func() string)(nil)"}, + {"%#v", reflect.ValueOf(U{}).Field(0), "(func() string)(nil)"}, + {"%#v", U{fn: fnValue}.fn, "(fmt_test.Fn)(nil)"}, // unexported field of function type with String method + {"%#v", reflect.ValueOf(U{fn: fnValue}.fn), "(fmt_test.Fn)(nil)"}, + {"%#v", reflect.ValueOf(U{fn: fnValue}).Field(1), "(fmt_test.Fn)(nil)"}, + // Whole number floats are printed without decimals. See Issue 27634. {"%#v", 1.0, "1"}, {"%#v", 1000000.0, "1e+06"}, @@ -1438,6 +1497,9 @@ var mallocTest = []struct { {0, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }}, {0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 7) }}, {0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 1<<16) }}, + {1, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); i := 1 << 16; Fprintf(&mallocBuf, "%x", i) }}, // not constant + {4, `Fprintf(buf, "%v")`, func() { mallocBuf.Reset(); s := []int{1, 2}; Fprintf(&mallocBuf, "%v", s) }}, + {1, `Fprintf(buf, "%v")`, func() { mallocBuf.Reset(); type P struct{ x, y int }; Fprintf(&mallocBuf, "%v", P{1, 2}) }}, {2, `Fprintf(buf, "%80000s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%80000s", "hello") }}, // large buffer (>64KB) // If the interface value doesn't need to allocate, amortized allocation overhead should be zero. {0, `Fprintf(buf, "%x %x %x")`, func() { @@ -1452,8 +1514,6 @@ func TestCountMallocs(t *testing.T) { switch { case testing.Short(): t.Skip("skipping malloc count in short mode") - case runtime.GOMAXPROCS(0) > 1: - t.Skip("skipping; GOMAXPROCS>1") case race.Enabled: t.Skip("skipping malloc count under race detector") } diff --git a/src/go.mod b/src/go.mod index 7a1318dcac32ba..c0bbca7e29bcc4 100644 --- a/src/go.mod +++ b/src/go.mod @@ -1,13 +1,13 @@ module std -go 1.24 +go 1.25 require ( - golang.org/x/crypto v0.30.0 - golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1 + golang.org/x/crypto v0.33.1-0.20250210163342-e47973b1c108 + golang.org/x/net v0.35.1-0.20250213222735-884432780bfd ) require ( - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect ) diff --git a/src/go.sum b/src/go.sum index 9e661352f16e0b..61223c0bbb6ee0 100644 --- a/src/go.sum +++ b/src/go.sum @@ -1,8 +1,8 @@ -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1 h1:+Yk1FZ5E+/ewA0nOO/HRYs9E4yeqpGOShuSAdzCNNoQ= -golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/crypto v0.33.1-0.20250210163342-e47973b1c108 h1:FwaGHNRX5GDt6vHr+Ly+yRTs0ADe4xTlGOzwaga4ZOs= +golang.org/x/crypto v0.33.1-0.20250210163342-e47973b1c108/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/net v0.35.1-0.20250213222735-884432780bfd h1:NtufTkm/X6BNpniJAbESf1Mvax5jGy+/oP53IEn5RiA= +golang.org/x/net v0.35.1-0.20250213222735-884432780bfd/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= diff --git a/src/go/build/build.go b/src/go/build/build.go index 9ffffda08a99b1..0e5c7e512d794c 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -1985,23 +1985,8 @@ func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool { } // other tags - for _, tag := range ctxt.BuildTags { - if tag == name { - return true - } - } - for _, tag := range ctxt.ToolTags { - if tag == name { - return true - } - } - for _, tag := range ctxt.ReleaseTags { - if tag == name { - return true - } - } - - return false + return slices.Contains(ctxt.BuildTags, name) || slices.Contains(ctxt.ToolTags, name) || + slices.Contains(ctxt.ReleaseTags, name) } // goodOSArchFile returns false if the name contains a $GOOS or $GOARCH diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index cc7f4df7f388ea..580500c033e1fc 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -58,6 +58,7 @@ var depsRules = ` internal/platform, internal/profilerecord, internal/syslist, + internal/trace/tracev2, internal/trace/traceviewer/format, log/internal, math/bits, @@ -79,6 +80,7 @@ var depsRules = ` internal/goexperiment, internal/goos, internal/profilerecord, + internal/trace/tracev2, math/bits, structs < internal/bytealg @@ -244,17 +246,17 @@ var depsRules = ` # encodings # core ones do not use fmt. io, strconv, slices - < encoding; + < encoding, encoding/base32, encoding/base64; encoding, reflect - < encoding/binary - < encoding/base32, encoding/base64; + < encoding/binary; FMT, encoding < flag; fmt !< encoding/base32, encoding/base64; - FMT, encoding/base32, encoding/base64, internal/saferio + FMT, encoding, encoding/base32, encoding/base64, encoding/binary, + internal/saferio < encoding/ascii85, encoding/csv, encoding/gob, encoding/hex, encoding/json, encoding/pem, encoding/xml, mime; @@ -388,12 +390,14 @@ var depsRules = ` os < golang.org/x/net/dns/dnsmessage, - golang.org/x/net/lif, - golang.org/x/net/route; + golang.org/x/net/lif; internal/bytealg, internal/itoa, math/bits, slices, strconv, unique < net/netip; + os, net/netip + < internal/routebsd; + # net is unavoidable when doing any networking, # so large dependencies must be kept out. # This is a long-looking list but most of these @@ -401,10 +405,10 @@ var depsRules = ` CGO, golang.org/x/net/dns/dnsmessage, golang.org/x/net/lif, - golang.org/x/net/route, internal/godebug, internal/nettrace, internal/poll, + internal/routebsd, internal/singleflight, net/netip, os, @@ -444,6 +448,10 @@ var depsRules = ` NET, log < net/mail; + # FIPS is the FIPS 140 module. + # It must not depend on external crypto packages. + # See also fips140deps.AllowedInternalPackages. + io, math/rand/v2 < crypto/internal/randutil; STR < crypto/internal/impl; @@ -455,8 +463,6 @@ var depsRules = ` internal/cpu, internal/goarch < crypto/internal/fips140deps/cpu; internal/godebug < crypto/internal/fips140deps/godebug; - # FIPS is the FIPS 140 module. - # It must not depend on external crypto packages. STR, crypto/internal/impl, crypto/internal/entropy, crypto/internal/randutil, @@ -491,63 +497,50 @@ var depsRules = ` < crypto/internal/fips140/rsa < FIPS; - FIPS < crypto/internal/fips140/check/checktest; + FIPS, internal/godebug < crypto/fips140; - FIPS, sync/atomic < crypto/tls/internal/fips140tls; + crypto, hash !< FIPS; - FIPS, internal/godebug, hash < crypto/fips140, crypto/internal/fips140only; + # CRYPTO is core crypto algorithms - no cgo, fmt, net. + # Mostly wrappers around the FIPS module. NONE < crypto/internal/boring/sig, crypto/internal/boring/syso; - sync/atomic < crypto/internal/boring/bcache, crypto/internal/boring/fips140tls; - crypto/internal/boring/sig, crypto/tls/internal/fips140tls < crypto/tls/fipsonly; + sync/atomic < crypto/internal/boring/bcache; - # CRYPTO is core crypto algorithms - no cgo, fmt, net. - FIPS, crypto/internal/fips140only, + FIPS, internal/godebug, hash, embed, crypto/internal/boring/sig, crypto/internal/boring/syso, - golang.org/x/sys/cpu, - hash, embed + crypto/internal/boring/bcache + < crypto/internal/fips140only < crypto < crypto/subtle + < crypto/sha3 + < crypto/internal/fips140hash < crypto/cipher - < crypto/sha3; - - crypto/cipher, - crypto/internal/boring/bcache < crypto/internal/boring - < crypto/boring; - - crypto/boring - < crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4, - crypto/sha1, crypto/sha256, crypto/sha512, crypto/hkdf; - - crypto/boring, crypto/internal/fips140/edwards25519/field - < crypto/ecdh; - - crypto/hmac < crypto/pbkdf2; - - crypto/internal/fips140/mlkem < crypto/mlkem; - - crypto/aes, - crypto/des, - crypto/ecdh, - crypto/hmac, - crypto/md5, - crypto/rc4, - crypto/sha1, - crypto/sha256, - crypto/sha512, - crypto/sha3, - crypto/hkdf + < crypto/boring + < crypto/aes, + crypto/des, + crypto/rc4, + crypto/md5, + crypto/sha1, + crypto/sha256, + crypto/sha512, + crypto/hmac, + crypto/hkdf, + crypto/pbkdf2, + crypto/ecdh, + crypto/mlkem < CRYPTO; CGO, fmt, net !< CRYPTO; - # CRYPTO-MATH is core bignum-based crypto - no cgo, net; fmt now ok. + # CRYPTO-MATH is crypto that exposes math/big APIs - no cgo, net; fmt now ok. + CRYPTO, FMT, math/big < crypto/internal/boring/bbig < crypto/rand - < crypto/ed25519 + < crypto/ed25519 # depends on crypto/rand.Reader < encoding/asn1 < golang.org/x/crypto/cryptobyte/asn1 < golang.org/x/crypto/cryptobyte @@ -558,23 +551,29 @@ var depsRules = ` CGO, net !< CRYPTO-MATH; # TLS, Prince of Dependencies. - CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem + + FIPS, sync/atomic < crypto/tls/internal/fips140tls; + + crypto/internal/boring/sig, crypto/tls/internal/fips140tls < crypto/tls/fipsonly; + + CRYPTO, golang.org/x/sys/cpu, encoding/binary, reflect < golang.org/x/crypto/internal/alias < golang.org/x/crypto/internal/subtle < golang.org/x/crypto/chacha20 < golang.org/x/crypto/internal/poly1305 - < golang.org/x/crypto/chacha20poly1305 + < golang.org/x/crypto/chacha20poly1305; + + CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem, + golang.org/x/crypto/chacha20poly1305, crypto/tls/internal/fips140tls < crypto/internal/hpke < crypto/x509/internal/macos - < crypto/x509/pkix; - - crypto/tls/internal/fips140tls, crypto/x509/pkix + < crypto/x509/pkix < crypto/x509 < crypto/tls; # crypto-aware packages - DEBUG, go/build, go/types, text/scanner, crypto/md5 + DEBUG, go/build, go/types, text/scanner, crypto/sha256 < internal/pkgbits, internal/exportdata < go/internal/gcimporter, go/internal/gccgoimporter, go/internal/srcimporter < go/importer; @@ -615,6 +614,7 @@ var depsRules = ` net/http/httptrace, mime/multipart, log + < net/http/internal/httpcommon < net/http; # HTTP-aware packages @@ -665,8 +665,9 @@ var depsRules = ` log/slog, testing < testing/slogtest; - FMT, crypto/sha256, encoding/json, go/ast, go/parser, go/token, - internal/godebug, math/rand, encoding/hex, crypto/sha256 + FMT, crypto/sha256, encoding/binary, encoding/json, + go/ast, go/parser, go/token, + internal/godebug, math/rand, encoding/hex < internal/fuzz; OS, flag, testing, internal/cfg, internal/platform, internal/goroot @@ -696,30 +697,27 @@ var depsRules = ` CGO, FMT < crypto/internal/sysrand/internal/seccomp; - # v2 execution trace parser. - FMT - < internal/trace/event; - - internal/trace/event - < internal/trace/event/go122; + FIPS + < crypto/internal/fips140/check/checktest; - FMT, io, internal/trace/event/go122 + # v2 execution trace parser. + FMT, io, internal/trace/tracev2 < internal/trace/version; FMT, encoding/binary, internal/trace/version < internal/trace/raw; - FMT, internal/trace/event, internal/trace/version, io, sort, encoding/binary - < internal/trace/internal/oldtrace; + FMT, internal/trace/version, io, sort, encoding/binary + < internal/trace/internal/tracev1; - FMT, encoding/binary, internal/trace/version, internal/trace/internal/oldtrace, container/heap, math/rand + FMT, encoding/binary, internal/trace/version, internal/trace/internal/tracev1, container/heap, math/rand < internal/trace; regexp, internal/trace, internal/trace/raw, internal/txtar < internal/trace/testtrace; regexp, internal/txtar, internal/trace, internal/trace/raw - < internal/trace/internal/testgen/go122; + < internal/trace/internal/testgen; # cmd/trace dependencies. FMT, diff --git a/src/go/doc/example.go b/src/go/doc/example.go index 0618f2bd9b8bab..7a8c26291db1f2 100644 --- a/src/go/doc/example.go +++ b/src/go/doc/example.go @@ -192,13 +192,6 @@ func playExample(file *ast.File, f *ast.FuncDecl) *ast.File { // Find unresolved identifiers and uses of top-level declarations. depDecls, unresolved := findDeclsAndUnresolved(body, topDecls, typMethods) - // Remove predeclared identifiers from unresolved list. - for n := range unresolved { - if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] { - delete(unresolved, n) - } - } - // Use unresolved identifiers to determine the imports used by this // example. The heuristic assumes package names match base import // paths for imports w/o renames (should be good enough most of the time). @@ -251,6 +244,13 @@ func playExample(file *ast.File, f *ast.FuncDecl) *ast.File { } } + // Remove predeclared identifiers from unresolved list. + for n := range unresolved { + if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] { + delete(unresolved, n) + } + } + // If there are other unresolved identifiers, give up because this // synthesized file is not going to build. if len(unresolved) > 0 { diff --git a/src/go/doc/testdata/examples/shadow_predeclared.go b/src/go/doc/testdata/examples/shadow_predeclared.go new file mode 100644 index 00000000000000..7e9f30d9b4e322 --- /dev/null +++ b/src/go/doc/testdata/examples/shadow_predeclared.go @@ -0,0 +1,19 @@ +// Copyright 2021 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. + +package foo_test + +import ( + "fmt" + + "example.com/error" +) + +func Print(s string) { + fmt.Println(s) +} + +func Example() { + Print(error.Hello) +} diff --git a/src/go/doc/testdata/examples/shadow_predeclared.golden b/src/go/doc/testdata/examples/shadow_predeclared.golden new file mode 100644 index 00000000000000..65598bed62d500 --- /dev/null +++ b/src/go/doc/testdata/examples/shadow_predeclared.golden @@ -0,0 +1,16 @@ +-- .Play -- +package main + +import ( + "fmt" + + "example.com/error" +) + +func Print(s string) { + fmt.Println(s) +} + +func main() { + Print(error.Hello) +} diff --git a/src/go/importer/importer.go b/src/go/importer/importer.go index 8a8fb0ec04030b..f0a1f651d2636c 100644 --- a/src/go/importer/importer.go +++ b/src/go/importer/importer.go @@ -3,6 +3,13 @@ // license that can be found in the LICENSE file. // Package importer provides access to export data importers. +// +// These functions, which are mostly deprecated, date from before the +// introduction of modules in release Go 1.11. They should no longer +// be relied on except for use in test cases using small programs that +// depend only on the standard library. For reliable module-aware +// loading of type information, use the packages.Load function from +// golang.org/x/tools/go/packages. package importer import ( @@ -73,12 +80,20 @@ func ForCompiler(fset *token.FileSet, compiler string, lookup Lookup) types.Impo // // Deprecated: Use [ForCompiler], which populates a FileSet // with the positions of objects created by the importer. +// +//go:fix inline func For(compiler string, lookup Lookup) types.Importer { return ForCompiler(token.NewFileSet(), compiler, lookup) } // Default returns an Importer for the compiler that built the running binary. // If available, the result implements [types.ImporterFrom]. +// +// Default may be convenient for use in the simplest of cases, but +// most clients should instead use [ForCompiler], which accepts a +// [token.FileSet] from the caller; without it, all position +// information derived from the Importer will be incorrect and +// misleading. See also the package documentation. func Default() types.Importer { return For(runtime.Compiler, nil) } diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 7260e963041871..c2906c5bda8a38 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -872,7 +872,7 @@ func (p *parser) parseParamDecl(name *ast.Ident, typeSetsOK bool) (f field) { return } -func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing token.Token) (params []*ast.Field) { +func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing token.Token, dddok bool) (params []*ast.Field) { if p.trace { defer un(trace(p, "ParameterList")) } @@ -931,7 +931,7 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok // distribute parameter types (len(list) > 0) if named == 0 { // all unnamed => found names are type names - for i := 0; i < len(list); i++ { + for i := range list { par := &list[i] if typ := par.name; typ != nil { par.typ = typ @@ -959,8 +959,8 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok // some named or we're in a type parameter list => all must be named var errPos token.Pos // left-most error position (or invalid) var typ ast.Expr // current type (from right to left) - for i := len(list) - 1; i >= 0; i-- { - if par := &list[i]; par.typ != nil { + for i := range list { + if par := &list[len(list)-i-1]; par.typ != nil { typ = par.typ if par.name == nil { errPos = typ.Pos() @@ -1006,6 +1006,26 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok } } + // check use of ... + first := true // only report first occurrence + for i, _ := range list { + f := &list[i] + if t, _ := f.typ.(*ast.Ellipsis); t != nil && (!dddok || i+1 < len(list)) { + if first { + first = false + if dddok { + p.error(t.Ellipsis, "can only use ... with final parameter") + } else { + p.error(t.Ellipsis, "invalid use of ...") + } + } + // use T instead of invalid ...T + // TODO(gri) would like to use `f.typ = t.Elt` but that causes problems + // with the resolver in cases of reuse of the same identifier + f.typ = &ast.BadExpr{From: t.Pos(), To: t.End()} + } + } + // Convert list to []*ast.Field. // If list contains types only, each type gets its own ast.Field. if named == 0 { @@ -1042,50 +1062,42 @@ func (p *parser) parseParameterList(name0 *ast.Ident, typ0 ast.Expr, closing tok return } -func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.FieldList) { +func (p *parser) parseTypeParameters() *ast.FieldList { if p.trace { - defer un(trace(p, "Parameters")) + defer un(trace(p, "TypeParameters")) } - if acceptTParams && p.tok == token.LBRACK { - opening := p.pos - p.next() - // [T any](params) syntax - list := p.parseParameterList(nil, nil, token.RBRACK) - rbrack := p.expect(token.RBRACK) - tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack} - // Type parameter lists must not be empty. - if tparams.NumFields() == 0 { - p.error(tparams.Closing, "empty type parameter list") - tparams = nil // avoid follow-on errors - } + lbrack := p.expect(token.LBRACK) + var list []*ast.Field + if p.tok != token.RBRACK { + list = p.parseParameterList(nil, nil, token.RBRACK, false) } + rbrack := p.expect(token.RBRACK) - opening := p.expect(token.LPAREN) - - var fields []*ast.Field - if p.tok != token.RPAREN { - fields = p.parseParameterList(nil, nil, token.RPAREN) + if len(list) == 0 { + p.error(rbrack, "empty type parameter list") + return nil // avoid follow-on errors } - rparen := p.expect(token.RPAREN) - params = &ast.FieldList{Opening: opening, List: fields, Closing: rparen} - - return + return &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack} } -func (p *parser) parseResult() *ast.FieldList { +func (p *parser) parseParameters(result bool) *ast.FieldList { if p.trace { - defer un(trace(p, "Result")) + defer un(trace(p, "Parameters")) } - if p.tok == token.LPAREN { - _, results := p.parseParameters(false) - return results + if !result || p.tok == token.LPAREN { + lparen := p.expect(token.LPAREN) + var list []*ast.Field + if p.tok != token.RPAREN { + list = p.parseParameterList(nil, nil, token.RPAREN, !result) + } + rparen := p.expect(token.RPAREN) + return &ast.FieldList{Opening: lparen, List: list, Closing: rparen} } - typ := p.tryIdentOrType() - if typ != nil { + if typ := p.tryIdentOrType(); typ != nil { list := make([]*ast.Field, 1) list[0] = &ast.Field{Type: typ} return &ast.FieldList{List: list} @@ -1100,11 +1112,15 @@ func (p *parser) parseFuncType() *ast.FuncType { } pos := p.expect(token.FUNC) - tparams, params := p.parseParameters(true) - if tparams != nil { - p.error(tparams.Pos(), "function type must have no type parameters") + // accept type parameters for more tolerant parsing but complain + if p.tok == token.LBRACK { + tparams := p.parseTypeParameters() + if tparams != nil { + p.error(tparams.Opening, "function type must have no type parameters") + } } - results := p.parseResult() + params := p.parseParameters(false) + results := p.parseParameters(true) return &ast.FuncType{Func: pos, Params: params, Results: results} } @@ -1132,13 +1148,13 @@ func (p *parser) parseMethodSpec() *ast.Field { // // Interface methods do not have type parameters. We parse them for a // better error message and improved error recovery. - _ = p.parseParameterList(name0, nil, token.RBRACK) + _ = p.parseParameterList(name0, nil, token.RBRACK, false) _ = p.expect(token.RBRACK) p.error(lbrack, "interface method must have no type parameters") // TODO(rfindley) refactor to share code with parseFuncType. - _, params := p.parseParameters(false) - results := p.parseResult() + params := p.parseParameters(false) + results := p.parseParameters(true) idents = []*ast.Ident{ident} typ = &ast.FuncType{ Func: token.NoPos, @@ -1167,8 +1183,8 @@ func (p *parser) parseMethodSpec() *ast.Field { case p.tok == token.LPAREN: // ordinary method // TODO(rfindley) refactor to share code with parseFuncType. - _, params := p.parseParameters(false) - results := p.parseResult() + params := p.parseParameters(false) + results := p.parseParameters(true) idents = []*ast.Ident{ident} typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results} default: @@ -2572,11 +2588,9 @@ func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 * defer un(trace(p, "parseGenericType")) } - list := p.parseParameterList(name0, typ0, token.RBRACK) + list := p.parseParameterList(name0, typ0, token.RBRACK, false) closePos := p.expect(token.RBRACK) spec.TypeParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos} - // Let the type checker decide whether to accept type parameters on aliases: - // see go.dev/issue/46477. if p.tok == token.ASSIGN { // type alias spec.Assign = p.pos @@ -2771,19 +2785,23 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { var recv *ast.FieldList if p.tok == token.LPAREN { - _, recv = p.parseParameters(false) + recv = p.parseParameters(false) } ident := p.parseIdent() - tparams, params := p.parseParameters(true) - if recv != nil && tparams != nil { - // Method declarations do not have type parameters. We parse them for a - // better error message and improved error recovery. - p.error(tparams.Opening, "method must have no type parameters") - tparams = nil + var tparams *ast.FieldList + if p.tok == token.LBRACK { + tparams = p.parseTypeParameters() + if recv != nil && tparams != nil { + // Method declarations do not have type parameters. We parse them for a + // better error message and improved error recovery. + p.error(tparams.Opening, "method must have no type parameters") + tparams = nil + } } - results := p.parseResult() + params := p.parseParameters(false) + results := p.parseParameters(true) var body *ast.BlockStmt switch p.tok { diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index 3a34e8c216bf96..9465fe0e478c55 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -190,6 +190,14 @@ var invalids = []string{ `package p; func f() { if true {} else ; /* ERROR "expected if statement or block" */ }`, `package p; func f() { if true {} else defer /* ERROR "expected if statement or block" */ f() }`, + // variadic parameter lists + `package p; func f(a, b ... /* ERROR "can only use ... with final parameter" */ int)`, + `package p; func f(a ... /* ERROR "can only use ... with final parameter" */ int, b int)`, + `package p; func f(... /* ERROR "can only use ... with final parameter" */ int, int)`, + `package p; func f() (... /* ERROR "invalid use of ..." */ int)`, + `package p; func f() (a, b ... /* ERROR "invalid use of ..." */ int)`, + `package p; func f[T ... /* ERROR "invalid use of ..." */ C]()() {}`, + // generic code `package p; type _[_ any] int; var _ = T[] /* ERROR "expected operand" */ {}`, `package p; var _ func[ /* ERROR "must have no type parameters" */ T any](T)`, diff --git a/src/go/types/api.go b/src/go/types/api.go index dea974bec80918..beb2258c8b6f47 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -217,11 +217,19 @@ type Info struct { // // The Types map does not record the type of every identifier, // only those that appear where an arbitrary expression is - // permitted. For instance, the identifier f in a selector - // expression x.f is found only in the Selections map, the - // identifier z in a variable declaration 'var z int' is found - // only in the Defs map, and identifiers denoting packages in - // qualified identifiers are collected in the Uses map. + // permitted. For instance: + // - an identifier f in a selector expression x.f is found + // only in the Selections map; + // - an identifier z in a variable declaration 'var z int' + // is found only in the Defs map; + // - an identifier p denoting a package in a qualified + // identifier p.X is found only in the Uses map. + // + // Similarly, no type is recorded for the (synthetic) FuncType + // node in a FuncDecl.Type field, since there is no corresponding + // syntactic function type expression in the source in this case + // Instead, the function type is found in the Defs.map entry for + // the corresponding function declaration. Types map[ast.Expr]TypeAndValue // Instances maps identifiers denoting generic types or functions to their diff --git a/src/go/types/api_test.go b/src/go/types/api_test.go index b686578b38278a..27b4ab8ea00413 100644 --- a/src/go/types/api_test.go +++ b/src/go/types/api_test.go @@ -19,11 +19,16 @@ import ( "testing" . "go/types" + "runtime" ) // nopos indicates an unknown position var nopos token.Pos +func defaultImporter(fset *token.FileSet) Importer { + return importer.ForCompiler(fset, runtime.Compiler, nil) +} + func mustParse(fset *token.FileSet, src string) *ast.File { f, err := parser.ParseFile(fset, pkgName(src), src, parser.ParseComments) if err != nil { @@ -33,12 +38,13 @@ func mustParse(fset *token.FileSet, src string) *ast.File { } func typecheck(src string, conf *Config, info *Info) (*Package, error) { + // TODO(adonovan): plumb this from caller. fset := token.NewFileSet() f := mustParse(fset, src) if conf == nil { conf = &Config{ Error: func(err error) {}, // collect all errors - Importer: importer.Default(), + Importer: defaultImporter(fset), } } return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) @@ -1128,7 +1134,7 @@ var ( Implicits: make(map[ast.Node]Object), } var conf Config - conf.Importer = importer.Default() + conf.Importer = defaultImporter(fset) _, err := conf.Check("p", fset, []*ast.File{f}, &info) if err != nil { t.Fatal(err) diff --git a/src/go/types/call.go b/src/go/types/call.go index 200068b1760054..4e8dfc0d6bdc9d 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -143,6 +143,9 @@ func (check *Checker) instantiateSignature(pos token.Pos, expr ast.Expr, typ *Si }() } + // For signatures, Checker.instance will always succeed because the type argument + // count is correct at this point (see assertion above); hence the type assertion + // to *Signature will always succeed. inst := check.instance(pos, typ, targs, nil, check.context()).(*Signature) assert(inst.TypeParams().Len() == 0) // signature is not generic anymore check.recordInstance(expr, targs, inst) diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index a10d0147da2c65..823525828ae3e4 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -34,7 +34,6 @@ import ( "flag" "fmt" "go/ast" - "go/importer" "go/parser" "go/scanner" "go/token" @@ -164,7 +163,7 @@ func testFilesImpl(t *testing.T, filenames []string, srcs [][]byte, manual bool, // set up typechecker var conf Config *boolFieldAddr(&conf, "_Trace") = manual && testing.Verbose() - conf.Importer = importer.Default() + conf.Importer = defaultImporter(fset) conf.Error = func(err error) { if *haltOnError { defer panic(err) diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go index b9afb9117f80c1..f7f0da0db95737 100644 --- a/src/go/types/eval_test.go +++ b/src/go/types/eval_test.go @@ -9,7 +9,6 @@ package types_test import ( "fmt" "go/ast" - "go/importer" "go/parser" "go/token" "go/types" @@ -188,7 +187,7 @@ func TestEvalPos(t *testing.T) { files = append(files, file) } - conf := Config{Importer: importer.Default()} + conf := Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("p", fset, files, nil) if err != nil { t.Fatal(err) @@ -209,7 +208,7 @@ func TestEvalPos(t *testing.T) { } // gotypesalias controls the use of Alias types. -var gotypesalias = godebug.New("#gotypesalias") +var gotypesalias = godebug.New("gotypesalias") // split splits string s at the first occurrence of s, trimming spaces. func split(s, sep string) (string, string) { @@ -257,7 +256,7 @@ func f(a int, s string) S { t.Fatal(err) } - conf := Config{Importer: importer.Default()} + conf := Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("p", fset, []*ast.File{f}, nil) if err != nil { t.Fatal(err) diff --git a/src/go/types/example_test.go b/src/go/types/example_test.go index 279771121aaaae..d8e5de7476c81b 100644 --- a/src/go/types/example_test.go +++ b/src/go/types/example_test.go @@ -19,7 +19,6 @@ import ( "fmt" "go/ast" "go/format" - "go/importer" "go/parser" "go/token" "go/types" @@ -57,7 +56,7 @@ func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get // Type-check a package consisting of these files. // Type information for the imported "fmt" package // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. - conf := types.Config{Importer: importer.Default()} + conf := types.Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("temperature", fset, files, nil) if err != nil { log.Fatal(err) @@ -126,7 +125,7 @@ type I interface { m() byte } // Type-check a package consisting of this file. // Type information for the imported packages // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. - conf := types.Config{Importer: importer.Default()} + conf := types.Config{Importer: defaultImporter(fset)} pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) diff --git a/src/go/types/expr.go b/src/go/types/expr.go index d4a08927015ac9..aaafe95eba0afb 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -147,27 +147,13 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { return case token.ARROW: - u := coreType(x.typ) - if u == nil { - check.errorf(x, InvalidReceive, invalidOp+"cannot receive from %s (no core type)", x) - x.mode = invalid + if elem := check.chanElem(x, x, true); elem != nil { + x.mode = commaok + x.typ = elem + check.hasCallOrRecv = true return } - ch, _ := u.(*Chan) - if ch == nil { - check.errorf(x, InvalidReceive, invalidOp+"cannot receive from non-channel %s", x) - x.mode = invalid - return - } - if ch.dir == SendOnly { - check.errorf(x, InvalidReceive, invalidOp+"cannot receive from send-only channel %s", x) - x.mode = invalid - return - } - - x.mode = commaok - x.typ = ch.elem - check.hasCallOrRecv = true + x.mode = invalid return case token.TILDE: @@ -205,6 +191,62 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr) { // x.typ remains unchanged } +// chanElem returns the channel element type of x for a receive from x (recv == true) +// or send to x (recv == false) operation. If the operation is not valid, chanElem +// reports an error and returns nil. +func (check *Checker) chanElem(pos positioner, x *operand, recv bool) Type { + var elem Type + var cause string + typeset(x.typ, func(t, u Type) bool { + if u == nil { + // Type set contains no explicit terms. + // It is either empty or contains all types (any) + cause = "no specific channel type" + return false + } + ch, _ := u.(*Chan) + if ch == nil { + cause = check.sprintf("non-channel %s", t) + return false + } + if recv && ch.dir == SendOnly { + cause = check.sprintf("send-only channel %s", t) + return false + } + if !recv && ch.dir == RecvOnly { + cause = check.sprintf("receive-only channel %s", t) + return false + } + if elem != nil && !Identical(elem, ch.elem) { + cause = check.sprintf("channels with different element types %s and %s", elem, ch.elem) + return false + } + elem = ch.elem + return true + }) + + if cause == "" { + return elem + } + + if recv { + if isTypeParam(x.typ) { + check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s: type set contains %s", x, cause) + } else { + // In this case, only the non-channel and send-only channel error are possible. + check.errorf(pos, InvalidReceive, invalidOp+"cannot receive from %s %s", cause, x) + } + } else { + if isTypeParam(x.typ) { + check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s: type set contains %s", x, cause) + } else { + // In this case, only the non-channel and receive-only channel error are possible. + check.errorf(pos, InvalidSend, invalidOp+"cannot send to %s %s", cause, x) + } + } + return nil +} + func isShift(op token.Token) bool { return op == token.SHL || op == token.SHR } @@ -1006,9 +1048,8 @@ func (check *Checker) exprInternal(T *target, x *operand, e ast.Expr, hint Type) check.ident(x, e, nil, false) case *ast.Ellipsis: - // ellipses are handled explicitly where they are legal - // (array composite literals and parameter lists) - check.error(e, BadDotDotDotSyntax, "invalid use of '...'") + // ellipses are handled explicitly where they are valid + check.error(e, InvalidSyntaxTree, "invalid use of ...") goto Error case *ast.BasicLit: diff --git a/src/go/types/initorder.go b/src/go/types/initorder.go index 7625c206674994..adf96fe718f13d 100644 --- a/src/go/types/initorder.go +++ b/src/go/types/initorder.go @@ -13,6 +13,7 @@ import ( "fmt" . "internal/types/errors" "slices" + "sort" ) // initOrder computes the Info.InitOrder for package variables. @@ -142,7 +143,16 @@ func findPath(objMap map[Object]*declInfo, from, to Object, seen map[Object]bool } seen[from] = true + // sort deps for deterministic result + var deps []Object for d := range objMap[from].deps { + deps = append(deps, d) + } + sort.Slice(deps, func(i, j int) bool { + return deps[i].order() < deps[j].order() + }) + + for _, d := range deps { if d == to { return []Object{d} } diff --git a/src/go/types/instantiate.go b/src/go/types/instantiate.go index 48eef7ca76c187..4b36312f96e75e 100644 --- a/src/go/types/instantiate.go +++ b/src/go/types/instantiate.go @@ -77,7 +77,8 @@ func Instantiate(ctxt *Context, orig Type, targs []Type, validate bool) (Type, e // instance instantiates the given original (generic) function or type with the // provided type arguments and returns the resulting instance. If an identical // instance exists already in the given contexts, it returns that instance, -// otherwise it creates a new one. +// otherwise it creates a new one. If there is an error (such as wrong number +// of type arguments), the result is Typ[Invalid]. // // If expanding is non-nil, it is the Named instance type currently being // expanded. If ctxt is non-nil, it is the context associated with the current @@ -136,9 +137,13 @@ func (check *Checker) instance(pos token.Pos, orig genericType, targs []Type, ex assert(expanding == nil) // Alias instances cannot be reached from Named types } + // verify type parameter count (see go.dev/issue/71198 for a test case) tparams := orig.TypeParams() - // TODO(gri) investigate if this is needed (type argument and parameter count seem to be correct here) - if !check.validateTArgLen(pos, orig.String(), tparams.Len(), len(targs)) { + if !check.validateTArgLen(pos, orig.obj.Name(), tparams.Len(), len(targs)) { + // TODO(gri) Consider returning a valid alias instance with invalid + // underlying (aliased) type to match behavior of *Named + // types. Then this function will never return an invalid + // result. return Typ[Invalid] } if tparams.Len() == 0 { diff --git a/src/go/types/interface.go b/src/go/types/interface.go index 01bbb08e0efe55..e5ca042e7509fc 100644 --- a/src/go/types/interface.go +++ b/src/go/types/interface.go @@ -176,17 +176,19 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d name := f.Names[0] if name.Name == "_" { check.error(name, BlankIfaceMethod, "methods must have a unique non-blank name") - continue // ignore + continue // ignore method } - typ := check.typ(f.Type) - sig, _ := typ.(*Signature) - if sig == nil { - if isValid(typ) { - check.errorf(f.Type, InvalidSyntaxTree, "%s is not a method signature", typ) - } - continue // ignore + // Type-check method declaration. + // Note: Don't call check.typ(f.Type) as that would record + // the method incorrectly as a type expression in Info.Types. + ftyp, _ := f.Type.(*ast.FuncType) + if ftyp == nil { + check.errorf(f.Type, InvalidSyntaxTree, "%s is not a method signature", f.Type) + continue // ignore method } + sig := new(Signature) + check.funcType(sig, nil, ftyp) // The go/parser doesn't accept method type parameters but an ast.FuncType may have them. if sig.tparams != nil { diff --git a/src/go/types/issues_test.go b/src/go/types/issues_test.go index 3eb34cf2d0dee5..f2c63f16f9aba6 100644 --- a/src/go/types/issues_test.go +++ b/src/go/types/issues_test.go @@ -9,7 +9,6 @@ package types_test import ( "fmt" "go/ast" - "go/importer" "go/parser" "go/token" "internal/testenv" @@ -291,7 +290,7 @@ func TestIssue25627(t *testing.T) { } { f := mustParse(fset, prefix+src) - cfg := Config{Importer: importer.Default(), Error: func(err error) {}} + cfg := Config{Importer: defaultImporter(fset), Error: func(err error) {}} info := &Info{Types: make(map[ast.Expr]TypeAndValue)} _, err := cfg.Check(f.Name.Name, fset, []*ast.File{f}, info) if err != nil { @@ -595,7 +594,11 @@ var _ T = template /* ERRORx "cannot use.*text/template.* as T value" */.Templat ) a := mustTypecheck(asrc, nil, nil) - imp := importHelper{pkg: a, fallback: importer.Default()} + imp := importHelper{ + pkg: a, + // TODO(adonovan): use same FileSet as mustTypecheck. + fallback: defaultImporter(token.NewFileSet()), + } withImporter := func(cfg *Config) { cfg.Importer = imp diff --git a/src/go/types/labels.go b/src/go/types/labels.go index 97b753581aedd9..7b6324880af6c0 100644 --- a/src/go/types/labels.go +++ b/src/go/types/labels.go @@ -119,8 +119,8 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.Labele fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...) } - var stmtBranches func(ast.Stmt) - stmtBranches = func(s ast.Stmt) { + var stmtBranches func(*ast.LabeledStmt, ast.Stmt) + stmtBranches = func(lstmt *ast.LabeledStmt, s ast.Stmt) { switch s := s.(type) { case *ast.DeclStmt: if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR { @@ -168,7 +168,7 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.Labele fwdJumps = fwdJumps[:i] lstmt = s } - stmtBranches(s.Stmt) + stmtBranches(lstmt, s.Stmt) case *ast.BranchStmt: if s.Label == nil { @@ -235,36 +235,36 @@ func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.Labele blockBranches(lstmt, s.List) case *ast.IfStmt: - stmtBranches(s.Body) + stmtBranches(lstmt, s.Body) if s.Else != nil { - stmtBranches(s.Else) + stmtBranches(lstmt, s.Else) } case *ast.CaseClause: blockBranches(nil, s.Body) case *ast.SwitchStmt: - stmtBranches(s.Body) + stmtBranches(lstmt, s.Body) case *ast.TypeSwitchStmt: - stmtBranches(s.Body) + stmtBranches(lstmt, s.Body) case *ast.CommClause: blockBranches(nil, s.Body) case *ast.SelectStmt: - stmtBranches(s.Body) + stmtBranches(lstmt, s.Body) case *ast.ForStmt: - stmtBranches(s.Body) + stmtBranches(lstmt, s.Body) case *ast.RangeStmt: - stmtBranches(s.Body) + stmtBranches(lstmt, s.Body) } } for _, s := range list { - stmtBranches(s) + stmtBranches(nil, s) } return fwdJumps diff --git a/src/go/types/lookup_test.go b/src/go/types/lookup_test.go index d3ca58b9fa16f2..e90a2ec89a32db 100644 --- a/src/go/types/lookup_test.go +++ b/src/go/types/lookup_test.go @@ -5,7 +5,6 @@ package types_test import ( - "go/importer" "go/token" "path/filepath" "runtime" @@ -28,7 +27,7 @@ func BenchmarkLookupFieldOrMethod(b *testing.B) { } conf := Config{ - Importer: importer.Default(), + Importer: defaultImporter(fset), } pkg, err := conf.Check("http", fset, files, nil) diff --git a/src/go/types/mono_test.go b/src/go/types/mono_test.go index ccab846c6dd6e3..d1f19ac5582fa1 100644 --- a/src/go/types/mono_test.go +++ b/src/go/types/mono_test.go @@ -7,7 +7,6 @@ package types_test import ( "errors" "fmt" - "go/importer" "go/types" "strings" "testing" @@ -19,7 +18,7 @@ func checkMono(t *testing.T, body string) error { var buf strings.Builder conf := types.Config{ Error: func(err error) { fmt.Fprintln(&buf, err) }, - Importer: importer.Default(), + Importer: defaultImporter(fset), // TODO(adonovan): use same FileSet as typecheck } typecheck(src, &conf, nil) if buf.Len() == 0 { diff --git a/src/go/types/resolver_test.go b/src/go/types/resolver_test.go index a83f1344de91c0..680ee69c97f4cc 100644 --- a/src/go/types/resolver_test.go +++ b/src/go/types/resolver_test.go @@ -7,7 +7,6 @@ package types_test import ( "fmt" "go/ast" - "go/importer" "go/token" "internal/testenv" "slices" @@ -17,6 +16,7 @@ import ( ) type resolveTestImporter struct { + fset *token.FileSet importer ImporterFrom imported map[string]bool } @@ -30,7 +30,7 @@ func (imp *resolveTestImporter) ImportFrom(path, srcDir string, mode ImportMode) panic("mode must be 0") } if imp.importer == nil { - imp.importer = importer.Default().(ImporterFrom) + imp.importer = defaultImporter(fset).(ImporterFrom) imp.imported = make(map[string]bool) } pkg, err := imp.importer.ImportFrom(path, srcDir, mode) @@ -124,7 +124,7 @@ func TestResolveIdents(t *testing.T) { } // resolve and type-check package AST - importer := new(resolveTestImporter) + importer := &resolveTestImporter{fset: fset} conf := Config{Importer: importer} uses := make(map[*ast.Ident]Object) defs := make(map[*ast.Ident]Object) diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go index 27fa75652a5204..b4cc6286a18f81 100644 --- a/src/go/types/self_test.go +++ b/src/go/types/self_test.go @@ -6,7 +6,6 @@ package types_test import ( "go/ast" - "go/importer" "go/parser" "go/token" "internal/testenv" @@ -27,7 +26,7 @@ func TestSelf(t *testing.T) { t.Fatal(err) } - conf := Config{Importer: importer.Default()} + conf := Config{Importer: defaultImporter(fset)} _, err = conf.Check("go/types", fset, files, nil) if err != nil { t.Fatal(err) @@ -82,7 +81,7 @@ func runbench(b *testing.B, path string, ignoreFuncBodies, writeInfo bool) { for i := 0; i < b.N; i++ { conf := Config{ IgnoreFuncBodies: ignoreFuncBodies, - Importer: importer.Default(), + Importer: defaultImporter(fset), } var info *Info if writeInfo { diff --git a/src/go/types/signature.go b/src/go/types/signature.go index 681eb85fd70b8f..365b1119393dd2 100644 --- a/src/go/types/signature.go +++ b/src/go/types/signature.go @@ -38,6 +38,8 @@ type Signature struct { // must be of unnamed slice type. // // Deprecated: Use [NewSignatureType] instead which allows for type parameters. +// +//go:fix inline func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { return NewSignatureType(recv, nil, nil, params, results, variadic) } @@ -195,7 +197,7 @@ func (check *Checker) collectRecv(rparam *ast.Field, scopePos token.Pos) (*Var, } else { // If there are type parameters, rbase must denote a generic base type. // Important: rbase must be resolved before declaring any receiver type - // parameters (wich may have the same name, see below). + // parameters (which may have the same name, see below). var baseType *Named // nil if not valid var cause string if t := check.genericType(rbase, &cause); isValid(t) { @@ -364,7 +366,7 @@ func (check *Checker) collectParams(list *ast.FieldList, variadicOk bool) (names if variadicOk && i == len(list.List)-1 && len(field.Names) <= 1 { variadic = true } else { - check.softErrorf(t, MisplacedDotDotDot, "can only use ... with final parameter in list") + check.softErrorf(t, InvalidSyntaxTree, "invalid use of ...") // ignore ... and continue } } diff --git a/src/go/types/sizes_test.go b/src/go/types/sizes_test.go index 825bc1f9f52ada..157faf87d4c575 100644 --- a/src/go/types/sizes_test.go +++ b/src/go/types/sizes_test.go @@ -8,7 +8,7 @@ package types_test import ( "go/ast" - "go/importer" + "go/token" "go/types" "internal/testenv" "testing" @@ -87,7 +87,8 @@ const _ = unsafe.Offsetof(struct{ x int64 }{}.x) ` info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} conf := types.Config{ - Importer: importer.Default(), + // TODO(adonovan): use same FileSet as mustTypecheck. + Importer: defaultImporter(token.NewFileSet()), Sizes: &types.StdSizes{WordSize: 8, MaxAlign: 8}, } mustTypecheck(src, &conf, &info) @@ -117,7 +118,8 @@ var s struct { for _, arch := range []string{"386", "amd64"} { t.Run(arch, func(t *testing.T) { conf := types.Config{ - Importer: importer.Default(), + // TODO(adonovan): use same FileSet as findStructTypeConfig. + Importer: defaultImporter(token.NewFileSet()), Sizes: types.SizesFor("gc", arch), } ts := findStructTypeConfig(t, src, &conf) @@ -188,7 +190,11 @@ func TestGCSizes(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() - conf := types.Config{Importer: importer.Default(), Sizes: types.SizesFor("gc", "amd64")} + conf := types.Config{ + // TODO(adonovan): use same FileSet as mustTypecheck. + Importer: defaultImporter(token.NewFileSet()), + Sizes: types.SizesFor("gc", "amd64"), + } mustTypecheck(tc.src, &conf, nil) }) } diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index d3223f3b92395f..d6a9fdd2de315f 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -466,21 +466,9 @@ func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) { if ch.mode == invalid || val.mode == invalid { return } - u := coreType(ch.typ) - if u == nil { - check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to %s: no core type", &ch) - return - } - uch, _ := u.(*Chan) - if uch == nil { - check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to non-channel %s", &ch) - return + if elem := check.chanElem(inNode(s, s.Arrow), &ch, false); elem != nil { + check.assignment(&val, elem, "send") } - if uch.dir == RecvOnly { - check.errorf(inNode(s, s.Arrow), InvalidSend, invalidOp+"cannot send to receive-only channel %s", &ch) - return - } - check.assignment(&val, uch.elem, "send") case *ast.IncDecStmt: var op token.Token @@ -1075,8 +1063,13 @@ func rangeKeyVal(typ Type, allowVersion func(goVersion) bool) (key, val Type, ca return bad("func must be func(yield func(...) bool): argument is not func") case cb.Params().Len() > 2: return bad("func must be func(yield func(...) bool): yield func has too many parameters") - case cb.Results().Len() != 1 || !isBoolean(cb.Results().At(0).Type()): - return bad("func must be func(yield func(...) bool): yield func does not return bool") + case cb.Results().Len() != 1 || !Identical(cb.Results().At(0).Type(), universeBool): + // see go.dev/issues/71131, go.dev/issues/71164 + if cb.Results().Len() == 1 && isBoolean(cb.Results().At(0).Type()) { + return bad("func must be func(yield func(...) bool): yield func returns user-defined boolean, not bool") + } else { + return bad("func must be func(yield func(...) bool): yield func does not return bool") + } } assert(cb.Recv() == nil) // determine key and value types, if any diff --git a/src/go/types/testdata/manual.go b/src/go/types/testdata/manual.go index d8f312f61d58c5..825ab50f92de49 100644 --- a/src/go/types/testdata/manual.go +++ b/src/go/types/testdata/manual.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Go Authors. All rights reserved. +// Copyright 2025 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. diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 5bcbc2d1d3b114..549a84b3cc700c 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -322,10 +322,8 @@ func (check *Checker) typInternal(e0 ast.Expr, def *TypeName) (T Type) { // report error if we encountered [...] case *ast.Ellipsis: - // dots are handled explicitly where they are legal - // (array composite literals and parameter lists) - check.error(e, InvalidDotDotDot, "invalid use of '...'") - check.use(e.Elt) + // dots are handled explicitly where they are valid + check.error(e, InvalidSyntaxTree, "invalid use of ...") case *ast.StructType: typ := new(Struct) @@ -419,11 +417,6 @@ func setDefType(def *TypeName, typ Type) { if def != nil { switch t := def.typ.(type) { case *Alias: - // t.fromRHS should always be set, either to an invalid type - // in the beginning, or to typ in certain cyclic declarations. - if t.fromRHS != Typ[Invalid] && t.fromRHS != typ { - panic(sprintf(nil, nil, true, "t.fromRHS = %s, typ = %s\n", t.fromRHS, typ)) - } t.fromRHS = typ case *Basic: assert(t == Typ[Invalid]) @@ -471,9 +464,14 @@ func (check *Checker) instantiatedType(ix *indexedExpr, def *TypeName) (res Type } // create instance - // The instance is not generic anymore as it has type arguments, but it still - // satisfies the genericType interface because it has type parameters, too. - inst := check.instance(ix.Pos(), gtyp, targs, nil, check.context()).(genericType) + // The instance is not generic anymore as it has type arguments, but unless + // instantiation failed, it still satisfies the genericType interface because + // it has type parameters, too. + ityp := check.instance(ix.Pos(), gtyp, targs, nil, check.context()) + inst, _ := ityp.(genericType) + if inst == nil { + return Typ[Invalid] + } // For Named types, orig.tparams may not be set up, so we need to do expansion later. check.later(func() { diff --git a/src/go/types/universe.go b/src/go/types/universe.go index 09b882ce055551..750a368278ab63 100644 --- a/src/go/types/universe.go +++ b/src/go/types/universe.go @@ -24,6 +24,7 @@ var Unsafe *Package var ( universeIota Object + universeBool Type universeByte Type // uint8 alias, but has name "byte" universeRune Type // int32 alias, but has name "rune" universeAnyNoAlias *TypeName @@ -278,6 +279,7 @@ func init() { defPredeclaredFuncs() universeIota = Universe.Lookup("iota") + universeBool = Universe.Lookup("bool").Type() universeByte = Universe.Lookup("byte").Type() universeRune = Universe.Lookup("rune").Type() universeError = Universe.Lookup("error").Type() diff --git a/src/internal/abi/switch.go b/src/internal/abi/switch.go index 9669fe51d5aa75..df6f99c945910f 100644 --- a/src/internal/abi/switch.go +++ b/src/internal/abi/switch.go @@ -27,12 +27,7 @@ type InterfaceSwitchCacheEntry struct { Itab uintptr } -const go122InterfaceSwitchCache = true - func UseInterfaceSwitchCache(goarch string) bool { - if !go122InterfaceSwitchCache { - return false - } // We need an atomic load instruction to make the cache multithreaded-safe. // (AtomicLoadPtr needs to be implemented in cmd/compile/internal/ssa/_gen/ARCH.rules.) switch goarch { diff --git a/src/internal/abi/type.go b/src/internal/abi/type.go index 1c1793fcf5ba37..4671b0da2848f3 100644 --- a/src/internal/abi/type.go +++ b/src/internal/abi/type.go @@ -187,11 +187,7 @@ func TypeOf(a any) *Type { // TypeFor returns the abi.Type for a type parameter. func TypeFor[T any]() *Type { - var v T - if t := TypeOf(v); t != nil { - return t // optimize for T being a non-interface kind - } - return TypeOf((*T)(nil)).Elem() // only for an interface kind + return (*PtrType)(unsafe.Pointer(TypeOf((*T)(nil)))).Elem } func (t *Type) Kind() Kind { return t.Kind_ & KindMask } diff --git a/src/internal/buildcfg/cfg.go b/src/internal/buildcfg/cfg.go index fca09bf8d3384a..5ae4c0c7adc8fe 100644 --- a/src/internal/buildcfg/cfg.go +++ b/src/internal/buildcfg/cfg.go @@ -307,8 +307,10 @@ func goriscv64() int { return 20 case "rva22u64": return 22 + case "rva23u64": + return 23 } - Error = fmt.Errorf("invalid GORISCV64: must be rva20u64, rva22u64") + Error = fmt.Errorf("invalid GORISCV64: must be rva20u64, rva22u64, rva23u64") v := DefaultGORISCV64[len("rva"):] i := strings.IndexFunc(v, func(r rune) bool { return r < '0' || r > '9' @@ -441,6 +443,9 @@ func gogoarchTags() []string { if GORISCV64 >= 22 { list = append(list, GOARCH+"."+"rva22u64") } + if GORISCV64 >= 23 { + list = append(list, GOARCH+"."+"rva23u64") + } return list case "wasm": var list []string diff --git a/src/internal/buildcfg/cfg_test.go b/src/internal/buildcfg/cfg_test.go index 757270b7788f27..2bbd478280241e 100644 --- a/src/internal/buildcfg/cfg_test.go +++ b/src/internal/buildcfg/cfg_test.go @@ -32,6 +32,10 @@ func TestConfigFlags(t *testing.T) { if goriscv64() != 22 { t.Errorf("Wrong parsing of RISCV64=rva22u64") } + os.Setenv("GORISCV64", "rva23u64") + if goriscv64() != 23 { + t.Errorf("Wrong parsing of RISCV64=rva23u64") + } Error = nil os.Setenv("GORISCV64", "rva22") if _ = goriscv64(); Error == nil { diff --git a/src/internal/buildcfg/exp.go b/src/internal/buildcfg/exp.go index 332c9afa576bb5..9c9ec2c711c150 100644 --- a/src/internal/buildcfg/exp.go +++ b/src/internal/buildcfg/exp.go @@ -74,13 +74,12 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) { } baseline := goexperiment.Flags{ - RegabiWrappers: regabiSupported, - RegabiArgs: regabiSupported, - CoverageRedesign: true, - AliasTypeParams: true, - SwissMap: true, - SpinbitMutex: haveXchg8, - SyncHashTrieMap: true, + RegabiWrappers: regabiSupported, + RegabiArgs: regabiSupported, + AliasTypeParams: true, + SwissMap: true, + SpinbitMutex: haveXchg8, + SyncHashTrieMap: true, } // Start with the statically enabled set of experiments. diff --git a/src/internal/coverage/cfile/emitdata_test.go b/src/internal/coverage/cfile/emitdata_test.go index c522048173e4c7..d127c6b2a976ba 100644 --- a/src/internal/coverage/cfile/emitdata_test.go +++ b/src/internal/coverage/cfile/emitdata_test.go @@ -7,7 +7,6 @@ package cfile import ( "fmt" "internal/coverage" - "internal/goexperiment" "internal/platform" "internal/testenv" "os" @@ -25,9 +24,6 @@ func TestCoverageApis(t *testing.T) { if testing.Short() { t.Skipf("skipping test: too long for short mode") } - if !goexperiment.CoverageRedesign { - t.Skipf("skipping new coverage tests (experiment not enabled)") - } testenv.MustHaveGoBuild(t) dir := t.TempDir() if fixedTestDir { @@ -465,9 +461,6 @@ func TestIssue56006EmitDataRaceCoverRunningGoroutine(t *testing.T) { if testing.Short() { t.Skipf("skipping test: too long for short mode") } - if !goexperiment.CoverageRedesign { - t.Skipf("skipping new coverage tests (experiment not enabled)") - } // This test requires "go test -race -cover", meaning that we need // go build, go run, and "-race" support. diff --git a/src/internal/coverage/cfile/testsupport.go b/src/internal/coverage/cfile/testsupport.go index 3594b32aee1a8d..adab47fd212d1e 100644 --- a/src/internal/coverage/cfile/testsupport.go +++ b/src/internal/coverage/cfile/testsupport.go @@ -109,7 +109,7 @@ func ProcessCoverTestDir(dir string, cfile string, cm string, cpkg string, w io. // Emit text output. if tf != nil { - if err := ts.cf.EmitTextual(tf); err != nil { + if err := ts.cf.EmitTextual(selpkgs, tf); err != nil { return err } tfClosed = true diff --git a/src/internal/coverage/cfile/ts_test.go b/src/internal/coverage/cfile/ts_test.go index d3441821a436d3..d106d3b132ce0a 100644 --- a/src/internal/coverage/cfile/ts_test.go +++ b/src/internal/coverage/cfile/ts_test.go @@ -8,7 +8,6 @@ import ( "encoding/json" "flag" "internal/coverage" - "internal/goexperiment" "internal/testenv" "os" "os/exec" @@ -32,9 +31,6 @@ func testGoCoverDir(t *testing.T) string { // relying on other test paths will provide a better signal when // running "go test -cover" for this package). func TestTestSupport(t *testing.T) { - if !goexperiment.CoverageRedesign { - return - } if testing.CoverMode() == "" { return } @@ -128,9 +124,6 @@ func genAuxMeta(t *testing.T, dstdir string) (string, string) { } func TestAuxMetaDataFiles(t *testing.T) { - if !goexperiment.CoverageRedesign { - return - } if testing.CoverMode() == "" { return } diff --git a/src/internal/coverage/cformat/fmt_test.go b/src/internal/coverage/cformat/fmt_test.go index d296939d5cd20c..a26de964c43538 100644 --- a/src/internal/coverage/cformat/fmt_test.go +++ b/src/internal/coverage/cformat/fmt_test.go @@ -47,8 +47,8 @@ func TestBasics(t *testing.T) { fm.AddUnit("lit.go", "f3", true, u, 0) } - var b1, b2, b3, b4 strings.Builder - if err := fm.EmitTextual(&b1); err != nil { + var b1, b2, b3, b4, b5 strings.Builder + if err := fm.EmitTextual(nil, &b1); err != nil { t.Fatalf("EmitTextual returned %v", err) } wantText := strings.TrimSpace(` @@ -64,6 +64,18 @@ lit.go:99.0,100.0 1 0`) t.Errorf("emit text: got:\n%s\nwant:\n%s\n", gotText, wantText) } + selected := []string{"my/pack2"} + if err := fm.EmitTextual(selected, &b5); err != nil { + t.Fatalf("EmitTextual returned %v", err) + } + wantText = strings.TrimSpace(` +mode: atomic +lit.go:99.0,100.0 1 0`) + gotText = strings.TrimSpace(b5.String()) + if wantText != gotText { + t.Errorf("emit text: got:\n%s\nwant:\n%s\n", gotText, wantText) + } + // Percent output with no aggregation. noCoverPkg := "" if err := fm.EmitPercent(&b2, nil, noCoverPkg, false, false); err != nil { diff --git a/src/internal/coverage/cformat/format.go b/src/internal/coverage/cformat/format.go index 4df0e70b81ca6e..01d3109e31fd9d 100644 --- a/src/internal/coverage/cformat/format.go +++ b/src/internal/coverage/cformat/format.go @@ -24,7 +24,7 @@ package cformat // } // } // myformatter.EmitPercent(os.Stdout, nil, "", true, true) -// myformatter.EmitTextual(somefile) +// myformatter.EmitTextual(nil, somefile) // // These apis are linked into tests that are built with "-cover", and // called at the end of test execution to produce text output or @@ -38,6 +38,7 @@ import ( "io" "maps" "slices" + "sort" "strings" "text/tabwriter" ) @@ -163,20 +164,31 @@ func (p *pstate) sortUnits(units []extcu) { }) } -// EmitTextual writes the accumulated coverage data in the legacy -// cmd/cover text format to the writer 'w'. We sort the data items by +// EmitTextual writes the accumulated coverage data for 'pkgs' in the legacy +// cmd/cover text format to the writer 'w'; if pkgs is empty, text output +// is emitted for all packages recorded. We sort the data items by // importpath, source file, and line number before emitting (this sorting // is not explicitly mandated by the format, but seems like a good idea // for repeatable/deterministic dumps). -func (fm *Formatter) EmitTextual(w io.Writer) error { +func (fm *Formatter) EmitTextual(pkgs []string, w io.Writer) error { if fm.cm == coverage.CtrModeInvalid { panic("internal error, counter mode unset") } + if len(pkgs) == 0 { + pkgs = make([]string, 0, len(fm.pm)) + for importpath := range fm.pm { + pkgs = append(pkgs, importpath) + } + } if _, err := fmt.Fprintf(w, "mode: %s\n", fm.cm.String()); err != nil { return err } - for _, importpath := range slices.Sorted(maps.Keys(fm.pm)) { + sort.Strings(pkgs) + for _, importpath := range pkgs { p := fm.pm[importpath] + if p == nil { + continue + } units := make([]extcu, 0, len(p.unitTable)) for u := range p.unitTable { units = append(units, u) diff --git a/src/internal/cpu/cpu.go b/src/internal/cpu/cpu.go index cd3db105238eec..81b8f7022e8e59 100644 --- a/src/internal/cpu/cpu.go +++ b/src/internal/cpu/cpu.go @@ -136,6 +136,17 @@ var S390X struct { _ CacheLinePad } +// RISCV64 contains the supported CPU features and performance characteristics for riscv64 +// platforms. The booleans in RISCV64, with the exception of HasFastMisaligned, indicate +// the presence of RISC-V extensions. +// The struct is padded to avoid false sharing. +var RISCV64 struct { + _ CacheLinePad + HasFastMisaligned bool // Fast misaligned accesses + HasV bool // Vector extension compatible with RVV 1.0 + _ CacheLinePad +} + // CPU feature variables are accessed by assembly code in various packages. //go:linkname X86 //go:linkname ARM @@ -144,6 +155,7 @@ var S390X struct { //go:linkname MIPS64X //go:linkname PPC64 //go:linkname S390X +//go:linkname RISCV64 // Initialize examines the processor and sets the relevant variables above. // This is called by the runtime package early in program initialization, diff --git a/src/internal/cpu/cpu_riscv64.go b/src/internal/cpu/cpu_riscv64.go index 2173fe88860904..e6e532c7e7e18f 100644 --- a/src/internal/cpu/cpu_riscv64.go +++ b/src/internal/cpu/cpu_riscv64.go @@ -6,5 +6,16 @@ package cpu const CacheLinePadSize = 64 +// RISC-V doesn't have a 'cpuid' equivalent. On Linux we rely on the riscv_hwprobe syscall. + func doinit() { + options = []option{ + {Name: "fastmisaligned", Feature: &RISCV64.HasFastMisaligned}, + {Name: "v", Feature: &RISCV64.HasV}, + } + osInit() +} + +func isSet(hwc uint, value uint) bool { + return hwc&value != 0 } diff --git a/src/internal/cpu/cpu_riscv64_linux.go b/src/internal/cpu/cpu_riscv64_linux.go new file mode 100644 index 00000000000000..a076d3e33ce2a4 --- /dev/null +++ b/src/internal/cpu/cpu_riscv64_linux.go @@ -0,0 +1,91 @@ +// Copyright 2024 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. + +//go:build riscv64 && linux + +package cpu + +import _ "unsafe" + +// RISC-V extension discovery code for Linux. +// +// A note on detection of the Vector extension using HWCAP. +// +// Support for the Vector extension version 1.0 was added to the Linux kernel in release 6.5. +// Support for the riscv_hwprobe syscall was added in 6.4. It follows that if the riscv_hwprobe +// syscall is not available then neither is the Vector extension (which needs kernel support). +// The riscv_hwprobe syscall should then be all we need to detect the Vector extension. +// However, some RISC-V board manufacturers ship boards with an older kernel on top of which +// they have back-ported various versions of the Vector extension patches but not the riscv_hwprobe +// patches. These kernels advertise support for the Vector extension using HWCAP. Falling +// back to HWCAP to detect the Vector extension, if riscv_hwprobe is not available, or simply not +// bothering with riscv_hwprobe at all and just using HWCAP may then seem like an attractive option. +// +// Unfortunately, simply checking the 'V' bit in AT_HWCAP will not work as this bit is used by +// RISC-V board and cloud instance providers to mean different things. The Lichee Pi 4A board +// and the Scaleway RV1 cloud instances use the 'V' bit to advertise their support for the unratified +// 0.7.1 version of the Vector Specification. The Banana Pi BPI-F3 and the CanMV-K230 board use +// it to advertise support for 1.0 of the Vector extension. Versions 0.7.1 and 1.0 of the Vector +// extension are binary incompatible. HWCAP can then not be used in isolation to populate the +// HasV field as this field indicates that the underlying CPU is compatible with RVV 1.0. +// Go will only support the ratified versions >= 1.0 and so any vector code it might generate +// would crash on a Scaleway RV1 instance or a Lichee Pi 4a, if allowed to run. +// +// There is a way at runtime to distinguish between versions 0.7.1 and 1.0 of the Vector +// specification by issuing a RVV 1.0 vsetvli instruction and checking the vill bit of the vtype +// register. This check would allow us to safely detect version 1.0 of the Vector extension +// with HWCAP, if riscv_hwprobe were not available. However, the check cannot +// be added until the assembler supports the Vector instructions. +// +// Note the riscv_hwprobe syscall does not suffer from these ambiguities by design as all of the +// extensions it advertises support for are explicitly versioned. It's also worth noting that +// the riscv_hwprobe syscall is the only way to detect multi-letter RISC-V extensions, e.g., Zvbb. +// These cannot be detected using HWCAP and so riscv_hwprobe must be used to detect the majority +// of RISC-V extensions. +// +// Please see https://docs.kernel.org/arch/riscv/hwprobe.html for more information. + +const ( + // Copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. + riscv_HWPROBE_KEY_IMA_EXT_0 = 0x4 + riscv_HWPROBE_IMA_V = 0x4 + riscv_HWPROBE_KEY_CPUPERF_0 = 0x5 + riscv_HWPROBE_MISALIGNED_FAST = 0x3 + riscv_HWPROBE_MISALIGNED_MASK = 0x7 +) + +// riscvHWProbePairs is copied from golang.org/x/sys/unix/ztypes_linux_riscv64.go. +type riscvHWProbePairs struct { + key int64 + value uint64 +} + +//go:linkname riscvHWProbe +func riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool + +func osInit() { + // A slice of key/value pair structures is passed to the RISCVHWProbe syscall. The key + // field should be initialised with one of the key constants defined above, e.g., + // RISCV_HWPROBE_KEY_IMA_EXT_0. The syscall will set the value field to the appropriate value. + // If the kernel does not recognise a key it will set the key field to -1 and the value field to 0. + + pairs := []riscvHWProbePairs{ + {riscv_HWPROBE_KEY_IMA_EXT_0, 0}, + {riscv_HWPROBE_KEY_CPUPERF_0, 0}, + } + + // This call only indicates that extensions are supported if they are implemented on all cores. + if !riscvHWProbe(pairs, 0) { + return + } + + if pairs[0].key != -1 { + v := uint(pairs[0].value) + RISCV64.HasV = isSet(v, riscv_HWPROBE_IMA_V) + } + if pairs[1].key != -1 { + v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK + RISCV64.HasFastMisaligned = v == riscv_HWPROBE_MISALIGNED_FAST + } +} diff --git a/src/internal/cpu/cpu_riscv64_other.go b/src/internal/cpu/cpu_riscv64_other.go new file mode 100644 index 00000000000000..1307d822b3257f --- /dev/null +++ b/src/internal/cpu/cpu_riscv64_other.go @@ -0,0 +1,11 @@ +// Copyright 2024 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. + +//go:build riscv64 && !linux + +package cpu + +func osInit() { + // Other operating systems do not support the riscv_hwprobe syscall. +} diff --git a/src/internal/exportdata/exportdata.go b/src/internal/exportdata/exportdata.go index 27675923b528d8..861a47f49f94f9 100644 --- a/src/internal/exportdata/exportdata.go +++ b/src/internal/exportdata/exportdata.go @@ -85,6 +85,7 @@ func ReadUnified(r *bufio.Reader) (data []byte, err error) { if n < 0 { err = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", size, n) + return } // Read n bytes from buf. diff --git a/src/internal/fuzz/coverage.go b/src/internal/fuzz/coverage.go index e214a7bf3e1846..8b39949b5dd622 100644 --- a/src/internal/fuzz/coverage.go +++ b/src/internal/fuzz/coverage.go @@ -23,11 +23,7 @@ func ResetCoverage() { func SnapshotCoverage() { cov := coverage() for i, b := range cov { - b |= b >> 1 - b |= b >> 2 - b |= b >> 4 - b -= b >> 1 - coverageSnapshot[i] = b + coverageSnapshot[i] = pow2Table[b] } } @@ -102,4 +98,18 @@ var ( // the 8-bit coverage counters reside in memory. They're known to cmd/link, // which specially assigns their addresses for this purpose. _counters, _ecounters [0]byte + + // lookup table for faster power of two rounding + pow2Table [256]byte ) + +func init() { + for i := range pow2Table { + b := byte(i) + b |= b >> 1 + b |= b >> 2 + b |= b >> 4 + b -= b >> 1 + pow2Table[i] = b + } +} diff --git a/src/internal/fuzz/mutators_byteslice_test.go b/src/internal/fuzz/mutators_byteslice_test.go index 56adca25372ed6..b12ef6cbcdd0bd 100644 --- a/src/internal/fuzz/mutators_byteslice_test.go +++ b/src/internal/fuzz/mutators_byteslice_test.go @@ -34,12 +34,6 @@ func (mr *mockRand) uint32n(n uint32) uint32 { return uint32(c) % n } -func (mr *mockRand) exp2() int { - c := mr.values[mr.counter] - mr.counter++ - return c -} - func (mr *mockRand) bool() bool { b := mr.b mr.b = !mr.b diff --git a/src/internal/fuzz/pcg.go b/src/internal/fuzz/pcg.go index dc07b9f5bdc437..b8251043f1c129 100644 --- a/src/internal/fuzz/pcg.go +++ b/src/internal/fuzz/pcg.go @@ -17,7 +17,6 @@ type mutatorRand interface { uint32() uint32 intn(int) int uint32n(uint32) uint32 - exp2() int bool() bool save(randState, randInc *uint64) @@ -123,11 +122,6 @@ func (r *pcgRand) uint32n(n uint32) uint32 { return uint32(prod >> 32) } -// exp2 generates n with probability 1/2^(n+1). -func (r *pcgRand) exp2() int { - return bits.TrailingZeros32(r.uint32()) -} - // bool generates a random bool. func (r *pcgRand) bool() bool { return r.uint32()&1 == 0 diff --git a/src/internal/godebug/godebug_test.go b/src/internal/godebug/godebug_test.go index 69296303560505..fe1e67225c9928 100644 --- a/src/internal/godebug/godebug_test.go +++ b/src/internal/godebug/godebug_test.go @@ -109,6 +109,9 @@ func TestCmdBisect(t *testing.T) { var want []string src, err := os.ReadFile("godebug_test.go") + if err != nil { + t.Fatal(err) + } for i, line := range strings.Split(string(src), "\n") { if strings.Contains(line, "BISECT"+" "+"BUG") { want = append(want, fmt.Sprintf("godebug_test.go:%d", i+1)) diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go index 9c48a923f032c3..e0fde01f09d9bb 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go @@ -28,6 +28,7 @@ var All = []Info{ {Name: "asynctimerchan", Package: "time", Changed: 23, Old: "1"}, {Name: "dataindependenttiming", Package: "crypto/subtle", Opaque: true}, {Name: "execerrdot", Package: "os/exec"}, + {Name: "fips140", Package: "crypto/fips140", Opaque: true}, {Name: "gocachehash", Package: "cmd/go"}, {Name: "gocachetest", Package: "cmd/go"}, {Name: "gocacheverify", Package: "cmd/go"}, diff --git a/src/internal/goexperiment/exp_coverageredesign_off.go b/src/internal/goexperiment/exp_coverageredesign_off.go deleted file mode 100644 index 2c33177322d1f4..00000000000000 --- a/src/internal/goexperiment/exp_coverageredesign_off.go +++ /dev/null @@ -1,8 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build !goexperiment.coverageredesign - -package goexperiment - -const CoverageRedesign = false -const CoverageRedesignInt = 0 diff --git a/src/internal/goexperiment/exp_coverageredesign_on.go b/src/internal/goexperiment/exp_coverageredesign_on.go deleted file mode 100644 index 3fc6c2f70a9ab5..00000000000000 --- a/src/internal/goexperiment/exp_coverageredesign_on.go +++ /dev/null @@ -1,8 +0,0 @@ -// Code generated by mkconsts.go. DO NOT EDIT. - -//go:build goexperiment.coverageredesign - -package goexperiment - -const CoverageRedesign = true -const CoverageRedesignInt = 1 diff --git a/src/internal/goexperiment/exp_synctest_off.go b/src/internal/goexperiment/exp_synctest_off.go new file mode 100644 index 00000000000000..fade13f89ca79c --- /dev/null +++ b/src/internal/goexperiment/exp_synctest_off.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build !goexperiment.synctest + +package goexperiment + +const Synctest = false +const SynctestInt = 0 diff --git a/src/internal/goexperiment/exp_synctest_on.go b/src/internal/goexperiment/exp_synctest_on.go new file mode 100644 index 00000000000000..9c44be7276138b --- /dev/null +++ b/src/internal/goexperiment/exp_synctest_on.go @@ -0,0 +1,8 @@ +// Code generated by mkconsts.go. DO NOT EDIT. + +//go:build goexperiment.synctest + +package goexperiment + +const Synctest = true +const SynctestInt = 1 diff --git a/src/internal/goexperiment/flags.go b/src/internal/goexperiment/flags.go index 31b3d0315b64f8..dff5255e000992 100644 --- a/src/internal/goexperiment/flags.go +++ b/src/internal/goexperiment/flags.go @@ -51,7 +51,7 @@ package goexperiment // tags, experiments use the strings.ToLower of their field name. // // For the baseline experimental configuration, see -// objabi.experimentBaseline. +// [internal/buildcfg.ParseGOEXPERIMENT]. // // If you change this struct definition, run "go generate". type Flags struct { @@ -83,10 +83,6 @@ type Flags struct { // by default. HeapMinimum512KiB bool - // CoverageRedesign enables the new compiler-based code coverage - // tooling. - CoverageRedesign bool - // Arenas causes the "arena" standard library package to be visible // to the outside world. Arenas bool diff --git a/src/internal/goos/gengoos.go b/src/internal/goos/gengoos.go index aba0d3c3356f4f..e0d4d38e898e89 100644 --- a/src/internal/goos/gengoos.go +++ b/src/internal/goos/gengoos.go @@ -17,7 +17,7 @@ import ( var gooses []string func main() { - data, err := os.ReadFile("../../internal/syslist/syslist..go") + data, err := os.ReadFile("../../internal/syslist/syslist.go") if err != nil { log.Fatal(err) } diff --git a/src/internal/goversion/goversion.go b/src/internal/goversion/goversion.go index de2bcf4c82eb33..18703a64eaa922 100644 --- a/src/internal/goversion/goversion.go +++ b/src/internal/goversion/goversion.go @@ -9,4 +9,4 @@ package goversion // // It should be updated at the start of each development cycle to be // the version of the next Go 1.x release. See go.dev/issue/40705. -const Version = 24 +const Version = 25 diff --git a/src/internal/pkgbits/encoder.go b/src/internal/pkgbits/encoder.go index c17a12399d0597..015842f58c06af 100644 --- a/src/internal/pkgbits/encoder.go +++ b/src/internal/pkgbits/encoder.go @@ -6,7 +6,7 @@ package pkgbits import ( "bytes" - "crypto/md5" + "crypto/sha256" "encoding/binary" "go/constant" "io" @@ -55,7 +55,7 @@ func NewPkgEncoder(version Version, syncFrames int) PkgEncoder { // DumpTo writes the package's encoded data to out0 and returns the // package fingerprint. func (pw *PkgEncoder) DumpTo(out0 io.Writer) (fingerprint [8]byte) { - h := md5.New() + h := sha256.New() out := io.MultiWriter(out0, h) writeUint32 := func(x uint32) { diff --git a/src/internal/poll/fd_unix.go b/src/internal/poll/fd_unix.go index 2535a3ae4dd43b..31e6e21120fde3 100644 --- a/src/internal/poll/fd_unix.go +++ b/src/internal/poll/fd_unix.go @@ -183,16 +183,9 @@ func (fd *FD) Pread(p []byte, off int64) (int, error) { if fd.IsStream && len(p) > maxRW { p = p[:maxRW] } - var ( - n int - err error - ) - for { - n, err = syscall.Pread(fd.Sysfd, p, off) - if err != syscall.EINTR { - break - } - } + n, err := ignoringEINTR2(func() (int, error) { + return syscall.Pread(fd.Sysfd, p, off) + }) if err != nil { n = 0 } diff --git a/src/internal/poll/sendfile_unix.go b/src/internal/poll/sendfile_unix.go index f5aee38a054683..1105e0569110fb 100644 --- a/src/internal/poll/sendfile_unix.go +++ b/src/internal/poll/sendfile_unix.go @@ -110,12 +110,20 @@ func sendFile(dstFD *FD, src int, offset *int64, size int64) (written int64, err // Retry. case syscall.ENOSYS, syscall.EOPNOTSUPP, syscall.EINVAL: // ENOSYS indicates no kernel support for sendfile. - // EINVAL indicates a FD type which does not support sendfile. + // EINVAL indicates a FD type that does not support sendfile. // // On Linux, copy_file_range can return EOPNOTSUPP when copying // to a NFS file (issue #40731); check for it here just in case. return written, err, written > 0 default: + // We want to handle ENOTSUP like EOPNOTSUPP. + // It's a pain to put it as a switch case + // because on Linux systems ENOTSUP == EOPNOTSUPP, + // so the compiler complains about a duplicate case. + if err == syscall.ENOTSUP { + return written, err, written > 0 + } + // Not a retryable error. return written, err, true } diff --git a/src/internal/reflectlite/reflect_mirror_test.go b/src/internal/reflectlite/reflect_mirror_test.go index c87573903454ed..8d13641516341a 100644 --- a/src/internal/reflectlite/reflect_mirror_test.go +++ b/src/internal/reflectlite/reflect_mirror_test.go @@ -13,6 +13,7 @@ import ( "os" "path/filepath" "runtime" + "slices" "strings" "sync" "testing" @@ -40,12 +41,7 @@ func newVisitor() visitor { return v } func (v visitor) filter(name string) bool { - for _, typeName := range typeNames { - if typeName == name { - return true - } - } - return false + return slices.Contains(typeNames, name) } func (v visitor) Visit(n ast.Node) ast.Visitor { diff --git a/src/internal/routebsd/address.go b/src/internal/routebsd/address.go new file mode 100644 index 00000000000000..aa1bc21d3f00ae --- /dev/null +++ b/src/internal/routebsd/address.go @@ -0,0 +1,300 @@ +// Copyright 2016 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. + +//go:build darwin || dragonfly || freebsd || netbsd || openbsd + +package routebsd + +import ( + "net/netip" + "runtime" + "syscall" +) + +// An Addr represents an address associated with packet routing. +type Addr interface { + // Family returns an address family. + Family() int +} + +// A LinkAddr represents a link-layer address. +type LinkAddr struct { + Index int // interface index when attached + Name string // interface name when attached + Addr []byte // link-layer address when attached +} + +// Family implements the Family method of Addr interface. +func (a *LinkAddr) Family() int { return syscall.AF_LINK } + +func parseLinkAddr(b []byte) (Addr, error) { + if len(b) < 8 { + return nil, errInvalidAddr + } + _, a, err := parseKernelLinkAddr(syscall.AF_LINK, b[4:]) + if err != nil { + return nil, err + } + a.(*LinkAddr).Index = int(nativeEndian.Uint16(b[2:4])) + return a, nil +} + +// parseKernelLinkAddr parses b as a link-layer address in +// conventional BSD kernel form. +func parseKernelLinkAddr(_ int, b []byte) (int, Addr, error) { + // The encoding looks like the following: + // +----------------------------+ + // | Type (1 octet) | + // +----------------------------+ + // | Name length (1 octet) | + // +----------------------------+ + // | Address length (1 octet) | + // +----------------------------+ + // | Selector length (1 octet) | + // +----------------------------+ + // | Data (variable) | + // +----------------------------+ + // + // On some platforms, all-bit-one of length field means "don't + // care". + nlen, alen, slen := int(b[1]), int(b[2]), int(b[3]) + if nlen == 0xff { + nlen = 0 + } + if alen == 0xff { + alen = 0 + } + if slen == 0xff { + slen = 0 + } + l := 4 + nlen + alen + slen + if len(b) < l { + return 0, nil, errInvalidAddr + } + data := b[4:] + var name string + var addr []byte + if nlen > 0 { + name = string(data[:nlen]) + data = data[nlen:] + } + if alen > 0 { + addr = data[:alen] + data = data[alen:] + } + return l, &LinkAddr{Name: name, Addr: addr}, nil +} + +// An InetAddr represent an internet address using IPv4 or IPv6. +type InetAddr struct { + IP netip.Addr +} + +func (a *InetAddr) Family() int { + if a.IP.Is4() { + return syscall.AF_INET + } else { + return syscall.AF_INET6 + } +} + +// parseInetAddr parses b as an internet address for IPv4 or IPv6. +func parseInetAddr(af int, b []byte) (Addr, error) { + const ( + off4 = 4 // offset of in_addr + off6 = 8 // offset of in6_addr + ipv4Len = 4 // length of IPv4 address in bytes + ipv6Len = 16 // length of IPv6 address in bytes + ) + switch af { + case syscall.AF_INET: + if len(b) < (off4+1) || len(b) < int(b[0]) { + return nil, errInvalidAddr + } + sockAddrLen := int(b[0]) + var ip [ipv4Len]byte + if sockAddrLen != 0 { + // Calculate how many bytes of the address to copy: + // either full IPv4 length or the available length. + n := off4 + ipv4Len + if sockAddrLen < n { + n = sockAddrLen + } + copy(ip[:], b[off4:n]) + } + a := &InetAddr{ + IP: netip.AddrFrom4(ip), + } + return a, nil + case syscall.AF_INET6: + if len(b) < (off6+1) || len(b) < int(b[0]) { + return nil, errInvalidAddr + } + var ip [ipv6Len]byte + sockAddrLen := int(b[0]) + if sockAddrLen != 0 { + n := off6 + ipv6Len + if sockAddrLen < n { + n = sockAddrLen + } + copy(ip[:], b[off6:n]) + if ip[0] == 0xfe && ip[1]&0xc0 == 0x80 || ip[0] == 0xff && (ip[1]&0x0f == 0x01 || ip[1]&0x0f == 0x02) { + // KAME based IPv6 protocol stack usually + // embeds the interface index in the + // interface-local or link-local address as + // the kernel-internal form. + id := int(bigEndian.Uint16(ip[2:4])) + if id != 0 { + ip[2], ip[3] = 0, 0 + } + } + } + // The kernel can provide an integer zone ID. + // We ignore it. + a := &InetAddr{ + IP: netip.AddrFrom16(ip), + } + return a, nil + default: + return nil, errInvalidAddr + } +} + +// parseKernelInetAddr parses b as an internet address in conventional +// BSD kernel form. +func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { + // The encoding looks similar to the NLRI encoding. + // +----------------------------+ + // | Length (1 octet) | + // +----------------------------+ + // | Address prefix (variable) | + // +----------------------------+ + // + // The differences between the kernel form and the NLRI + // encoding are: + // + // - The length field of the kernel form indicates the prefix + // length in bytes, not in bits + // + // - In the kernel form, zero value of the length field + // doesn't mean 0.0.0.0/0 or ::/0 + // + // - The kernel form appends leading bytes to the prefix field + // to make the tuple to be conformed with + // the routing message boundary + l := int(b[0]) + if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { + // On Darwin, an address in the kernel form is also + // used as a message filler. + if l == 0 || len(b) > roundup(l) { + l = roundup(l) + } + } else { + l = roundup(l) + } + if len(b) < l { + return 0, nil, errInvalidAddr + } + // Don't reorder case expressions. + // The case expressions for IPv6 must come first. + const ( + off4 = 4 // offset of in_addr + off6 = 8 // offset of in6_addr + ) + switch { + case b[0] == syscall.SizeofSockaddrInet6: + a := &InetAddr{ + IP: netip.AddrFrom16([16]byte(b[off6 : off6+16])), + } + return int(b[0]), a, nil + case af == syscall.AF_INET6: + var ab [16]byte + if l-1 < off6 { + copy(ab[:], b[1:l]) + } else { + copy(ab[:], b[l-off6:l]) + } + a := &InetAddr{ + IP: netip.AddrFrom16(ab), + } + return int(b[0]), a, nil + case b[0] == syscall.SizeofSockaddrInet4: + a := &InetAddr{ + IP: netip.AddrFrom4([4]byte(b[off4 : off4+4])), + } + return int(b[0]), a, nil + default: // an old fashion, AF_UNSPEC or unknown means AF_INET + var ab [4]byte + if l-1 < off4 { + copy(ab[:], b[1:l]) + } else { + copy(ab[:], b[l-off4:l]) + } + a := &InetAddr{ + IP: netip.AddrFrom4(ab), + } + return int(b[0]), a, nil + } +} + +func parseAddrs(attrs uint, b []byte) ([]Addr, error) { + var as [syscall.RTAX_MAX]Addr + af := int(syscall.AF_UNSPEC) + for i := uint(0); i < syscall.RTAX_MAX && len(b) >= roundup(0); i++ { + if attrs&(1< 4 { @@ -52,7 +32,7 @@ func ParseRIB(typ RIBType, b []byte) ([]Message, error) { if w, ok := wireFormats[int(b[3])]; !ok { nskips++ } else { - m, err := w.parse(typ, b[:l]) + m, err := w.parse(b[:l]) if err != nil { return nil, err } @@ -64,6 +44,7 @@ func ParseRIB(typ RIBType, b []byte) ([]Message, error) { } b = b[l:] } + // We failed to parse any of the messages - version mismatch? if nmsgs != len(msgs)+nskips { return nil, errMessageMismatch diff --git a/src/internal/routebsd/message_darwin_test.go b/src/internal/routebsd/message_darwin_test.go new file mode 100644 index 00000000000000..f224f0a73d4547 --- /dev/null +++ b/src/internal/routebsd/message_darwin_test.go @@ -0,0 +1,28 @@ +// Copyright 2016 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. + +package routebsd + +import ( + "syscall" + "testing" +) + +func TestFetchRIBMessagesOnDarwin(t *testing.T) { + for _, typ := range []int{syscall.NET_RT_FLAGS, syscall.NET_RT_DUMP2, syscall.NET_RT_IFLIST2} { + ms, err := FetchRIBMessages(typ, 0) + if err != nil { + t.Error(typ, err) + continue + } + ss, err := msgs(ms).validate() + if err != nil { + t.Error(typ, err) + continue + } + for _, s := range ss { + t.Log(s) + } + } +} diff --git a/src/internal/routebsd/message_freebsd_test.go b/src/internal/routebsd/message_freebsd_test.go new file mode 100644 index 00000000000000..f0065198c8f2ef --- /dev/null +++ b/src/internal/routebsd/message_freebsd_test.go @@ -0,0 +1,28 @@ +// Copyright 2016 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. + +package routebsd + +import ( + "syscall" + "testing" +) + +func TestFetchRIBMessagesOnFreeBSD(t *testing.T) { + for _, typ := range []int{syscall.NET_RT_IFMALIST} { + ms, err := FetchRIBMessages(typ, 0) + if err != nil { + t.Error(typ, err) + continue + } + ss, err := msgs(ms).validate() + if err != nil { + t.Error(typ, err) + continue + } + for _, s := range ss { + t.Log(s) + } + } +} diff --git a/src/internal/routebsd/message_test.go b/src/internal/routebsd/message_test.go new file mode 100644 index 00000000000000..958f2010628a06 --- /dev/null +++ b/src/internal/routebsd/message_test.go @@ -0,0 +1,51 @@ +// Copyright 2016 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. + +//go:build darwin || dragonfly || freebsd || netbsd || openbsd + +package routebsd + +import ( + "syscall" + "testing" +) + +func TestFetchRIBMessages(t *testing.T) { + for _, typ := range []int{syscall.NET_RT_DUMP, syscall.NET_RT_IFLIST} { + ms, err := FetchRIBMessages(typ, 0) + if err != nil { + t.Error(typ, err) + continue + } + ss, err := msgs(ms).validate() + if err != nil { + t.Error(typ, err) + continue + } + for _, s := range ss { + t.Log(typ, s) + } + } +} + +func TestParseRIBWithFuzz(t *testing.T) { + for _, fuzz := range []string{ + "0\x00\x05\x050000000000000000" + + "00000000000000000000" + + "00000000000000000000" + + "00000000000000000000" + + "0000000000000\x02000000" + + "00000000", + "\x02\x00\x05\f0000000000000000" + + "0\x0200000000000000", + "\x02\x00\x05\x100000000000000\x1200" + + "0\x00\xff\x00", + "\x02\x00\x05\f0000000000000000" + + "0\x12000\x00\x02\x0000", + "\x00\x00\x00\x01\x00", + "00000", + } { + parseRIB([]byte(fuzz)) + } +} diff --git a/src/internal/routebsd/route.go b/src/internal/routebsd/route.go new file mode 100644 index 00000000000000..5b6062e125869d --- /dev/null +++ b/src/internal/routebsd/route.go @@ -0,0 +1,59 @@ +// Copyright 2016 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. + +//go:build darwin || dragonfly || freebsd || netbsd || openbsd + +// Package routebsd supports reading interface addresses on BSD systems. +// This is a very stripped down version of x/net/route, +// for use by the net package in the standard library. +package routebsd + +import ( + "errors" + "syscall" +) + +var ( + errMessageMismatch = errors.New("message mismatch") + errMessageTooShort = errors.New("message too short") + errInvalidMessage = errors.New("invalid message") + errInvalidAddr = errors.New("invalid address") +) + +// fetchRIB fetches a routing information base from the operating +// system. +// +// The arg is an interface index or 0 for all. +func fetchRIB(typ, arg int) ([]byte, error) { + try := 0 + for { + try++ + b, err := syscall.RouteRIB(typ, arg) + + // If the sysctl failed because the data got larger + // between the two sysctl calls, try a few times + // before failing (issue #45736). + const maxTries = 3 + if err == syscall.ENOMEM && try < maxTries { + continue + } + + return b, err + } +} + +// FetchRIBMessages fetches a list of addressing messages for an interface. +// The typ argument is something like syscall.NET_RT_IFLIST. +// The argument is an interface index or 0 for all. +func FetchRIBMessages(typ, arg int) ([]Message, error) { + b, err := fetchRIB(typ, arg) + if err != nil { + return nil, err + } + ms, err := parseRIB(b) + if err != nil { + return nil, err + } + return ms, nil +} diff --git a/src/internal/routebsd/route_test.go b/src/internal/routebsd/route_test.go new file mode 100644 index 00000000000000..613175e388eceb --- /dev/null +++ b/src/internal/routebsd/route_test.go @@ -0,0 +1,239 @@ +// Copyright 2016 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. + +//go:build darwin || dragonfly || freebsd || netbsd || openbsd + +package routebsd + +import ( + "fmt" + "runtime" + "syscall" +) + +func (m *InterfaceMessage) String() string { + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + return fmt.Sprintf("%s", attrs) +} + +func (m *InterfaceAddrMessage) String() string { + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + return fmt.Sprintf("%s", attrs) +} + +func (m *InterfaceMulticastAddrMessage) String() string { + return fmt.Sprintf("%s", addrAttrs(nativeEndian.Uint32(m.raw[4:8]))) +} + +type addrAttrs uint + +var addrAttrNames = [...]string{ + "dst", + "gateway", + "netmask", + "genmask", + "ifp", + "ifa", + "author", + "brd", + "df:mpls1-n:tag-o:src", // mpls1 for dragonfly, tag for netbsd, src for openbsd + "df:mpls2-o:srcmask", // mpls2 for dragonfly, srcmask for openbsd + "df:mpls3-o:label", // mpls3 for dragonfly, label for openbsd + "o:bfd", // bfd for openbsd + "o:dns", // dns for openbsd + "o:static", // static for openbsd + "o:search", // search for openbsd +} + +func (attrs addrAttrs) String() string { + var s string + for i, name := range addrAttrNames { + if attrs&(1<" + } + return s +} + +type msgs []Message + +func (ms msgs) validate() ([]string, error) { + var ss []string + for _, m := range ms { + switch m := m.(type) { + case *InterfaceMessage: + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + if err := addrs(m.Addrs).match(attrs); err != nil { + return nil, err + } + ss = append(ss, m.String()+" "+addrs(m.Addrs).String()) + case *InterfaceAddrMessage: + var attrs addrAttrs + if runtime.GOOS == "openbsd" { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[12:16])) + } else { + attrs = addrAttrs(nativeEndian.Uint32(m.raw[4:8])) + } + if err := addrs(m.Addrs).match(attrs); err != nil { + return nil, err + } + ss = append(ss, m.String()+" "+addrs(m.Addrs).String()) + case *InterfaceMulticastAddrMessage: + if err := addrs(m.Addrs).match(addrAttrs(nativeEndian.Uint32(m.raw[4:8]))); err != nil { + return nil, err + } + ss = append(ss, m.String()+" "+addrs(m.Addrs).String()) + default: + ss = append(ss, fmt.Sprintf("%+v", m)) + } + } + return ss, nil +} + +type addrFamily int + +func (af addrFamily) String() string { + switch af { + case syscall.AF_UNSPEC: + return "unspec" + case syscall.AF_LINK: + return "link" + case syscall.AF_INET: + return "inet4" + case syscall.AF_INET6: + return "inet6" + default: + return fmt.Sprintf("%d", af) + } +} + +const hexDigit = "0123456789abcdef" + +type llAddr []byte + +func (a llAddr) String() string { + if len(a) == 0 { + return "" + } + buf := make([]byte, 0, len(a)*3-1) + for i, b := range a { + if i > 0 { + buf = append(buf, ':') + } + buf = append(buf, hexDigit[b>>4]) + buf = append(buf, hexDigit[b&0xF]) + } + return string(buf) +} + +type ipAddr []byte + +func (a ipAddr) String() string { + if len(a) == 0 { + return "" + } + if len(a) == 4 { + return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3]) + } + if len(a) == 16 { + return fmt.Sprintf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]) + } + s := make([]byte, len(a)*2) + for i, tn := range a { + s[i*2], s[i*2+1] = hexDigit[tn>>4], hexDigit[tn&0xf] + } + return string(s) +} + +func (a *LinkAddr) String() string { + name := a.Name + if name == "" { + name = "" + } + lla := llAddr(a.Addr).String() + if lla == "" { + lla = "" + } + return fmt.Sprintf("(%v %d %s %s)", addrFamily(a.Family()), a.Index, name, lla) +} + +func (a *InetAddr) String() string { + return fmt.Sprintf("(%v %v)", addrFamily(a.Family()), a.IP) +} + +type addrs []Addr + +func (as addrs) String() string { + var s string + for _, a := range as { + if a == nil { + continue + } + if len(s) > 0 { + s += " " + } + switch a := a.(type) { + case *LinkAddr: + s += a.String() + case *InetAddr: + s += a.String() + } + } + if s == "" { + return "" + } + return s +} + +func (as addrs) match(attrs addrAttrs) error { + var ts addrAttrs + af := syscall.AF_UNSPEC + for i := range as { + if as[i] != nil { + ts |= 1 << uint(i) + } + switch addr := as[i].(type) { + case *InetAddr: + got := 0 + if addr.IP.Is4() { + got = syscall.AF_INET + } else if addr.IP.Is6() { + got = syscall.AF_INET6 + } + if af == syscall.AF_UNSPEC { + if got != 0 { + af = got + } + } + if got != 0 && af != got { + return fmt.Errorf("got %v; want %v", addrs(as), addrFamily(af)) + } + } + } + if ts != attrs && ts > attrs { + return fmt.Errorf("%v not included in %v", ts, attrs) + } + return nil +} diff --git a/src/vendor/golang.org/x/net/route/sys.go b/src/internal/routebsd/sys.go similarity index 93% rename from src/vendor/golang.org/x/net/route/sys.go rename to src/internal/routebsd/sys.go index fcebee58ec6da2..8805bca97e7cf2 100644 --- a/src/vendor/golang.org/x/net/route/sys.go +++ b/src/internal/routebsd/sys.go @@ -4,7 +4,7 @@ //go:build darwin || dragonfly || freebsd || netbsd || openbsd -package route +package routebsd import ( "syscall" @@ -41,5 +41,5 @@ func roundup(l int) int { type wireFormat struct { extOff int // offset of header extension bodyOff int // offset of message body - parse func(RIBType, []byte) (Message, error) + parse func([]byte) (Message, error) } diff --git a/src/internal/routebsd/sys_darwin.go b/src/internal/routebsd/sys_darwin.go new file mode 100644 index 00000000000000..14864e2e327df2 --- /dev/null +++ b/src/internal/routebsd/sys_darwin.go @@ -0,0 +1,38 @@ +// Copyright 2016 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. + +package routebsd + +import "syscall" + +// MTU returns the interface MTU. +func (m *InterfaceMessage) MTU() int { + return int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])) +} + +// sizeofIfMsghdr2 is copied from x/sys/unix. +const sizeofIfMsghdr2 = 0xa0 + +func probeRoutingStack() (int, map[int]*wireFormat) { + ifm := &wireFormat{extOff: 16, bodyOff: syscall.SizeofIfMsghdr} + ifm.parse = ifm.parseInterfaceMessage + ifm2 := &wireFormat{extOff: 32, bodyOff: sizeofIfMsghdr2} + ifm2.parse = ifm2.parseInterfaceMessage + ifam := &wireFormat{extOff: syscall.SizeofIfaMsghdr, bodyOff: syscall.SizeofIfaMsghdr} + ifam.parse = ifam.parseInterfaceAddrMessage + ifmam := &wireFormat{extOff: syscall.SizeofIfmaMsghdr, bodyOff: syscall.SizeofIfmaMsghdr} + ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage + ifmam2 := &wireFormat{extOff: syscall.SizeofIfmaMsghdr2, bodyOff: syscall.SizeofIfmaMsghdr2} + ifmam2.parse = ifmam2.parseInterfaceMulticastAddrMessage + // Darwin kernels require 32-bit aligned access to routing facilities. + return 4, map[int]*wireFormat{ + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_NEWMADDR: ifmam, + syscall.RTM_DELMADDR: ifmam, + syscall.RTM_IFINFO2: ifm2, + syscall.RTM_NEWMADDR2: ifmam2, + } +} diff --git a/src/internal/routebsd/sys_dragonfly.go b/src/internal/routebsd/sys_dragonfly.go new file mode 100644 index 00000000000000..ce0e6fd4039245 --- /dev/null +++ b/src/internal/routebsd/sys_dragonfly.go @@ -0,0 +1,41 @@ +// Copyright 2016 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. + +package routebsd + +import ( + "syscall" + "unsafe" +) + +// MTU returns the interface MTU. +func (m *InterfaceMessage) MTU() int { + return int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])) +} + +func probeRoutingStack() (int, map[int]*wireFormat) { + var p uintptr + ifm := &wireFormat{extOff: 16, bodyOff: syscall.SizeofIfMsghdr} + ifm.parse = ifm.parseInterfaceMessage + ifam := &wireFormat{extOff: syscall.SizeofIfaMsghdr, bodyOff: syscall.SizeofIfaMsghdr} + ifam.parse = ifam.parseInterfaceAddrMessage + ifmam := &wireFormat{extOff: syscall.SizeofIfmaMsghdr, bodyOff: syscall.SizeofIfmaMsghdr} + ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage + + rel, _ := syscall.SysctlUint32("kern.osreldate") + if rel >= 500705 { + // https://github.com/DragonFlyBSD/DragonFlyBSD/commit/43a373152df2d405c9940983e584e6a25e76632d + // but only the size of struct ifa_msghdr actually changed + rtmVersion = 7 + ifam.bodyOff = 0x18 + } + + return int(unsafe.Sizeof(p)), map[int]*wireFormat{ + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_NEWMADDR: ifmam, + syscall.RTM_DELMADDR: ifmam, + } +} diff --git a/src/internal/routebsd/sys_freebsd.go b/src/internal/routebsd/sys_freebsd.go new file mode 100644 index 00000000000000..5d5f49e42e01ea --- /dev/null +++ b/src/internal/routebsd/sys_freebsd.go @@ -0,0 +1,62 @@ +// Copyright 2016 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. + +package routebsd + +import ( + "syscall" + "unsafe" +) + +// MTU returns the interface MTU. +func (m *InterfaceMessage) MTU() int { + return int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])) +} + +// sizeofIfMsghdr is the size used on FreeBSD 11 for all platforms. +const sizeofIfMsghdr = 0xa8 + +func probeRoutingStack() (int, map[int]*wireFormat) { + var p uintptr + wordSize := int(unsafe.Sizeof(p)) + align := wordSize + // In the case of kern.supported_archs="amd64 i386", we need + // to know the underlying kernel's architecture because the + // alignment for routing facilities are set at the build time + // of the kernel. + conf, _ := syscall.Sysctl("kern.conftxt") + for i, j := 0, 0; j < len(conf); j++ { + if conf[j] != '\n' { + continue + } + s := conf[i:j] + i = j + 1 + if len(s) > len("machine") && s[:len("machine")] == "machine" { + s = s[len("machine"):] + for k := 0; k < len(s); k++ { + if s[k] == ' ' || s[k] == '\t' { + s = s[1:] + } + break + } + if s == "amd64" { + align = 8 + } + break + } + } + ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdr} + ifm.parse = ifm.parseInterfaceMessage + ifam := &wireFormat{extOff: syscall.SizeofIfaMsghdr, bodyOff: syscall.SizeofIfaMsghdr} + ifam.parse = ifam.parseInterfaceAddrMessage + ifmam := &wireFormat{extOff: syscall.SizeofIfmaMsghdr, bodyOff: syscall.SizeofIfmaMsghdr} + ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage + return align, map[int]*wireFormat{ + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + syscall.RTM_NEWMADDR: ifmam, + syscall.RTM_DELMADDR: ifmam, + } +} diff --git a/src/internal/routebsd/sys_netbsd.go b/src/internal/routebsd/sys_netbsd.go new file mode 100644 index 00000000000000..b181f727c25ca5 --- /dev/null +++ b/src/internal/routebsd/sys_netbsd.go @@ -0,0 +1,26 @@ +// Copyright 2016 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. + +package routebsd + +import "syscall" + +// MTU returns the interface MTU. +func (m *InterfaceMessage) MTU() int { + return int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])) +} + +func probeRoutingStack() (int, map[int]*wireFormat) { + ifm := &wireFormat{extOff: 16, bodyOff: syscall.SizeofIfMsghdr} + ifm.parse = ifm.parseInterfaceMessage + ifam := &wireFormat{extOff: syscall.SizeofIfaMsghdr, bodyOff: syscall.SizeofIfaMsghdr} + ifam.parse = ifam.parseInterfaceAddrMessage + // NetBSD 6 and above kernels require 64-bit aligned access to + // routing facilities. + return 8, map[int]*wireFormat{ + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + } +} diff --git a/src/internal/routebsd/sys_openbsd.go b/src/internal/routebsd/sys_openbsd.go new file mode 100644 index 00000000000000..83256c8fabfa59 --- /dev/null +++ b/src/internal/routebsd/sys_openbsd.go @@ -0,0 +1,28 @@ +// Copyright 2016 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. + +package routebsd + +import ( + "syscall" + "unsafe" +) + +// MTU returns the interface MTU. +func (m *InterfaceMessage) MTU() int { + return int(nativeEndian.Uint32(m.raw[28:32])) +} + +func probeRoutingStack() (int, map[int]*wireFormat) { + var p uintptr + ifm := &wireFormat{extOff: -1, bodyOff: -1} + ifm.parse = ifm.parseInterfaceMessage + ifam := &wireFormat{extOff: -1, bodyOff: -1} + ifam.parse = ifam.parseInterfaceAddrMessage + return int(unsafe.Sizeof(p)), map[int]*wireFormat{ + syscall.RTM_NEWADDR: ifam, + syscall.RTM_DELADDR: ifam, + syscall.RTM_IFINFO: ifm, + } +} diff --git a/src/internal/runtime/maps/group.go b/src/internal/runtime/maps/group.go index f3e9d4d12b9b7e..6414ee5b9b03b9 100644 --- a/src/internal/runtime/maps/group.go +++ b/src/internal/runtime/maps/group.go @@ -111,7 +111,7 @@ func bitsetShiftOutLowest(b bitset) bitset { // // empty: 1 0 0 0 0 0 0 0 // deleted: 1 1 1 1 1 1 1 0 -// full: 0 h h h h h h h // h represents the H1 hash bits +// full: 0 h h h h h h h // h represents the H2 hash bits // // TODO(prattmic): Consider inverting the top bit so that the zero value is empty. type ctrl uint8 diff --git a/src/internal/runtime/maps/map.go b/src/internal/runtime/maps/map.go index ffafcacdea1bc7..62463351c7d2de 100644 --- a/src/internal/runtime/maps/map.go +++ b/src/internal/runtime/maps/map.go @@ -194,6 +194,7 @@ func h2(h uintptr) uintptr { type Map struct { // The number of filled slots (i.e. the number of elements in all // tables). Excludes deleted slots. + // Must be first (known by the compiler, for len() builtin). used uint64 // seed is the hash seed, computed as a unique random number per map. diff --git a/src/internal/runtime/maps/map_swiss_test.go b/src/internal/runtime/maps/map_swiss_test.go index 4e02f3e66076b6..6da006413ac61d 100644 --- a/src/internal/runtime/maps/map_swiss_test.go +++ b/src/internal/runtime/maps/map_swiss_test.go @@ -50,7 +50,6 @@ func TestTableGroupCount(t *testing.T) { var testCases = []struct { n int // n is the number of map elements escape mapCase // expected values for escaping map - // TODO(go.dev/issue/54766): implement stack allocated maps }{ { n: -(1 << 30), diff --git a/src/internal/sync/hashtriemap.go b/src/internal/sync/hashtriemap.go index d31d81df39ca19..6f5e0b437fea23 100644 --- a/src/internal/sync/hashtriemap.go +++ b/src/internal/sync/hashtriemap.go @@ -79,7 +79,7 @@ func (ht *HashTrieMap[K, V]) Load(key K) (value V, ok bool) { } i = n.indirect() } - panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating") + panic("internal/sync.HashTrieMap: ran out of hash bits while iterating") } // LoadOrStore returns the existing value for the key if present. @@ -120,7 +120,7 @@ func (ht *HashTrieMap[K, V]) LoadOrStore(key K, value V) (result V, loaded bool) i = n.indirect() } if !haveInsertPoint { - panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating") + panic("internal/sync.HashTrieMap: ran out of hash bits while iterating") } // Grab the lock and double-check what we saw. @@ -178,7 +178,7 @@ func (ht *HashTrieMap[K, V]) expand(oldEntry, newEntry *entry[K, V], newHash uin top := newIndirect for { if hashShift == 0 { - panic("internal/concurrent.HashMapTrie: ran out of hash bits while inserting") + panic("internal/sync.HashTrieMap: ran out of hash bits while inserting") } hashShift -= nChildrenLog2 // hashShift is for the level parent is at. We need to go deeper. oi := (oldHash >> hashShift) & nChildrenMask @@ -219,26 +219,16 @@ func (ht *HashTrieMap[K, V]) Swap(key K, new V) (previous V, loaded bool) { slot = &i.children[(hash>>hashShift)&nChildrenMask] n = slot.Load() - if n == nil { + if n == nil || n.isEntry { // We found a nil slot which is a candidate for insertion, // or an existing entry that we'll replace. haveInsertPoint = true break } - if n.isEntry { - // Swap if the keys compare. - old, swapped := n.entry().swap(key, new) - if swapped { - return old, true - } - // If we fail, that means we should try to insert. - haveInsertPoint = true - break - } i = n.indirect() } if !haveInsertPoint { - panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating") + panic("internal/sync.HashTrieMap: ran out of hash bits while iterating") } // Grab the lock and double-check what we saw. @@ -261,10 +251,11 @@ func (ht *HashTrieMap[K, V]) Swap(key K, new V) (previous V, loaded bool) { var zero V var oldEntry *entry[K, V] if n != nil { - // Between before and now, something got inserted. Swap if the keys compare. + // Swap if the keys compare. oldEntry = n.entry() - old, swapped := oldEntry.swap(key, new) + newEntry, old, swapped := oldEntry.swap(key, new) if swapped { + slot.Store(&newEntry.node) return old, true } } @@ -292,30 +283,25 @@ func (ht *HashTrieMap[K, V]) CompareAndSwap(key K, old, new V) (swapped bool) { panic("called CompareAndSwap when value is not of comparable type") } hash := ht.keyHash(abi.NoEscape(unsafe.Pointer(&key)), ht.seed) - for { - // Find the key or return if it's not there. - i := ht.root.Load() - hashShift := 8 * goarch.PtrSize - found := false - for hashShift != 0 { - hashShift -= nChildrenLog2 - slot := &i.children[(hash>>hashShift)&nChildrenMask] - n := slot.Load() - if n == nil { - // Nothing to compare with. Give up. - return false - } - if n.isEntry { - // We found an entry. Try to compare and swap directly. - return n.entry().compareAndSwap(key, old, new, ht.valEqual) - } - i = n.indirect() - } - if !found { - panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating") - } + // Find a node with the key and compare with it. n != nil if we found the node. + i, _, slot, n := ht.find(key, hash, ht.valEqual, old) + if i != nil { + defer i.mu.Unlock() + } + if n == nil { + return false + } + + // Try to swap the entry. + e, swapped := n.entry().compareAndSwap(key, old, new, ht.valEqual) + if !swapped { + // Nothing was actually swapped, which means the node is no longer there. + return false } + // Store the entry back because it changed. + slot.Store(&e.node) + return true } // LoadAndDelete deletes the value for a key, returning the previous value if any. @@ -353,7 +339,7 @@ func (ht *HashTrieMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) { // Check if the node is now empty (and isn't the root), and delete it if able. for i.parent != nil && i.empty() { if hashShift == 8*goarch.PtrSize { - panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating") + panic("internal/sync.HashTrieMap: ran out of hash bits while iterating") } hashShift += nChildrenLog2 @@ -415,7 +401,7 @@ func (ht *HashTrieMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) { // Check if the node is now empty (and isn't the root), and delete it if able. for i.parent != nil && i.empty() { if hashShift == 8*goarch.PtrSize { - panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating") + panic("internal/sync.HashTrieMap: ran out of hash bits while iterating") } hashShift += nChildrenLog2 @@ -468,7 +454,7 @@ func (ht *HashTrieMap[K, V]) find(key K, hash uintptr, valEqual equalFunc, value i = n.indirect() } if !found { - panic("internal/concurrent.HashMapTrie: ran out of hash bits while iterating") + panic("internal/sync.HashTrieMap: ran out of hash bits while iterating") } // Grab the lock and double-check what we saw. @@ -523,7 +509,7 @@ func (ht *HashTrieMap[K, V]) iter(i *indirect[K, V], yield func(key K, value V) } e := n.entry() for e != nil { - if !yield(e.key, *e.value.Load()) { + if !yield(e.key, e.value) { return false } e = e.overflow.Load() @@ -579,22 +565,21 @@ type entry[K comparable, V any] struct { node[K, V] overflow atomic.Pointer[entry[K, V]] // Overflow for hash collisions. key K - value atomic.Pointer[V] + value V } func newEntryNode[K comparable, V any](key K, value V) *entry[K, V] { - e := &entry[K, V]{ - node: node[K, V]{isEntry: true}, - key: key, + return &entry[K, V]{ + node: node[K, V]{isEntry: true}, + key: key, + value: value, } - e.value.Store(&value) - return e } func (e *entry[K, V]) lookup(key K) (V, bool) { for e != nil { if e.key == key { - return *e.value.Load(), true + return e.value, true } e = e.overflow.Load() } @@ -603,87 +588,69 @@ func (e *entry[K, V]) lookup(key K) (V, bool) { func (e *entry[K, V]) lookupWithValue(key K, value V, valEqual equalFunc) (V, bool) { for e != nil { - oldp := e.value.Load() - if e.key == key && (valEqual == nil || valEqual(unsafe.Pointer(oldp), abi.NoEscape(unsafe.Pointer(&value)))) { - return *oldp, true + if e.key == key && (valEqual == nil || valEqual(unsafe.Pointer(&e.value), abi.NoEscape(unsafe.Pointer(&value)))) { + return e.value, true } e = e.overflow.Load() } return *new(V), false } -// swap replaces a value in the overflow chain if keys compare equal. -// Returns the old value, and whether or not anything was swapped. +// swap replaces an entry in the overflow chain if keys compare equal. Returns the new entry chain, +// the old value, and whether or not anything was swapped. // // swap must be called under the mutex of the indirect node which e is a child of. -func (head *entry[K, V]) swap(key K, newv V) (V, bool) { +func (head *entry[K, V]) swap(key K, new V) (*entry[K, V], V, bool) { if head.key == key { - vp := new(V) - *vp = newv - oldp := head.value.Swap(vp) - return *oldp, true + // Return the new head of the list. + e := newEntryNode(key, new) + if chain := head.overflow.Load(); chain != nil { + e.overflow.Store(chain) + } + return e, head.value, true } i := &head.overflow e := i.Load() for e != nil { if e.key == key { - vp := new(V) - *vp = newv - oldp := e.value.Swap(vp) - return *oldp, true + eNew := newEntryNode(key, new) + eNew.overflow.Store(e.overflow.Load()) + i.Store(eNew) + return head, e.value, true } i = &e.overflow e = e.overflow.Load() } var zero V - return zero, false + return head, zero, false } -// compareAndSwap replaces a value for a matching key and existing value in the overflow chain. -// Returns whether or not anything was swapped. +// compareAndSwap replaces an entry in the overflow chain if both the key and value compare +// equal. Returns the new entry chain and whether or not anything was swapped. // // compareAndSwap must be called under the mutex of the indirect node which e is a child of. -func (head *entry[K, V]) compareAndSwap(key K, oldv, newv V, valEqual equalFunc) bool { - var vbox *V -outerLoop: - for { - oldvp := head.value.Load() - if head.key == key && valEqual(unsafe.Pointer(oldvp), abi.NoEscape(unsafe.Pointer(&oldv))) { - // Return the new head of the list. - if vbox == nil { - // Delay explicit creation of a new value to hold newv. If we just pass &newv - // to CompareAndSwap, then newv will unconditionally escape, even if the CAS fails. - vbox = new(V) - *vbox = newv - } - if head.value.CompareAndSwap(oldvp, vbox) { - return true - } - // We need to restart from the head of the overflow list in case, due to a removal, a node - // is moved up the list and we miss it. - continue outerLoop +func (head *entry[K, V]) compareAndSwap(key K, old, new V, valEqual equalFunc) (*entry[K, V], bool) { + if head.key == key && valEqual(unsafe.Pointer(&head.value), abi.NoEscape(unsafe.Pointer(&old))) { + // Return the new head of the list. + e := newEntryNode(key, new) + if chain := head.overflow.Load(); chain != nil { + e.overflow.Store(chain) } - i := &head.overflow - e := i.Load() - for e != nil { - oldvp := e.value.Load() - if e.key == key && valEqual(unsafe.Pointer(oldvp), abi.NoEscape(unsafe.Pointer(&oldv))) { - if vbox == nil { - // Delay explicit creation of a new value to hold newv. If we just pass &newv - // to CompareAndSwap, then newv will unconditionally escape, even if the CAS fails. - vbox = new(V) - *vbox = newv - } - if e.value.CompareAndSwap(oldvp, vbox) { - return true - } - continue outerLoop - } - i = &e.overflow - e = e.overflow.Load() + return e, true + } + i := &head.overflow + e := i.Load() + for e != nil { + if e.key == key && valEqual(unsafe.Pointer(&e.value), abi.NoEscape(unsafe.Pointer(&old))) { + eNew := newEntryNode(key, new) + eNew.overflow.Store(e.overflow.Load()) + i.Store(eNew) + return head, true } - return false + i = &e.overflow + e = e.overflow.Load() } + return head, false } // loadAndDelete deletes an entry in the overflow chain by key. Returns the value for the key, the new @@ -693,14 +660,14 @@ outerLoop: func (head *entry[K, V]) loadAndDelete(key K) (V, *entry[K, V], bool) { if head.key == key { // Drop the head of the list. - return *head.value.Load(), head.overflow.Load(), true + return head.value, head.overflow.Load(), true } i := &head.overflow e := i.Load() for e != nil { if e.key == key { i.Store(e.overflow.Load()) - return *e.value.Load(), head, true + return e.value, head, true } i = &e.overflow e = e.overflow.Load() @@ -713,14 +680,14 @@ func (head *entry[K, V]) loadAndDelete(key K) (V, *entry[K, V], bool) { // // compareAndDelete must be called under the mutex of the indirect node which e is a child of. func (head *entry[K, V]) compareAndDelete(key K, value V, valEqual equalFunc) (*entry[K, V], bool) { - if head.key == key && valEqual(unsafe.Pointer(head.value.Load()), abi.NoEscape(unsafe.Pointer(&value))) { + if head.key == key && valEqual(unsafe.Pointer(&head.value), abi.NoEscape(unsafe.Pointer(&value))) { // Drop the head of the list. return head.overflow.Load(), true } i := &head.overflow e := i.Load() for e != nil { - if e.key == key && valEqual(unsafe.Pointer(e.value.Load()), abi.NoEscape(unsafe.Pointer(&value))) { + if e.key == key && valEqual(unsafe.Pointer(&e.value), abi.NoEscape(unsafe.Pointer(&value))) { i.Store(e.overflow.Load()) return head, true } diff --git a/src/internal/sync/hashtriemap_test.go b/src/internal/sync/hashtriemap_test.go index 5476add8803681..d9219f841a8bdf 100644 --- a/src/internal/sync/hashtriemap_test.go +++ b/src/internal/sync/hashtriemap_test.go @@ -12,6 +12,7 @@ import ( "strconv" "sync" "testing" + "weak" ) func TestHashTrieMap(t *testing.T) { @@ -921,3 +922,61 @@ func init() { testDataLarge[i] = fmt.Sprintf("%b", i) } } + +// TestConcurrentCache tests HashTrieMap in a scenario where it is used as +// the basis of a memory-efficient concurrent cache. We're specifically +// looking to make sure that CompareAndSwap and CompareAndDelete are +// atomic with respect to one another. When competing for the same +// key-value pair, they must not both succeed. +// +// This test is a regression test for issue #70970. +func TestConcurrentCache(t *testing.T) { + type dummy [32]byte + + var m isync.HashTrieMap[int, weak.Pointer[dummy]] + + type cleanupArg struct { + key int + value weak.Pointer[dummy] + } + cleanup := func(arg cleanupArg) { + m.CompareAndDelete(arg.key, arg.value) + } + get := func(m *isync.HashTrieMap[int, weak.Pointer[dummy]], key int) *dummy { + nv := new(dummy) + nw := weak.Make(nv) + for { + w, loaded := m.LoadOrStore(key, nw) + if !loaded { + runtime.AddCleanup(nv, cleanup, cleanupArg{key, nw}) + return nv + } + if v := w.Value(); v != nil { + return v + } + + // Weak pointer was reclaimed, try to replace it with nw. + if m.CompareAndSwap(key, w, nw) { + runtime.AddCleanup(nv, cleanup, cleanupArg{key, nw}) + return nv + } + } + } + + const N = 100_000 + const P = 5_000 + + var wg sync.WaitGroup + wg.Add(N) + for i := range N { + go func() { + defer wg.Done() + a := get(&m, i%P) + b := get(&m, i%P) + if a != b { + t.Errorf("consecutive cache reads returned different values: a != b (%p vs %p)\n", a, b) + } + }() + } + wg.Wait() +} diff --git a/src/internal/synctest/synctest_test.go b/src/internal/synctest/synctest_test.go index 7d1e04e2ba34e2..450d5f54166943 100644 --- a/src/internal/synctest/synctest_test.go +++ b/src/internal/synctest/synctest_test.go @@ -433,6 +433,32 @@ func TestWaitGroup(t *testing.T) { }) } +func TestHappensBefore(t *testing.T) { + var v int + synctest.Run(func() { + go func() { + v++ // 1 + }() + // Wait creates a happens-before relationship on the above goroutine exiting. + synctest.Wait() + go func() { + v++ // 2 + }() + }) + // Run exiting creates a happens-before relationship on goroutines started in the bubble. + synctest.Run(func() { + v++ // 3 + // There is a happens-before relationship between the time.AfterFunc call, + // and the func running. + time.AfterFunc(0, func() { + v++ // 4 + }) + }) + if got, want := v, 4; got != want { + t.Errorf("v = %v, want %v", got, want) + } +} + func wantPanic(t *testing.T, want string) { if e := recover(); e != nil { if got := fmt.Sprint(e); got != want { diff --git a/src/internal/syscall/unix/asm_darwin.s b/src/internal/syscall/unix/asm_darwin.s index b96eb1e80752fa..0f28cd1e393bc7 100644 --- a/src/internal/syscall/unix/asm_darwin.s +++ b/src/internal/syscall/unix/asm_darwin.s @@ -25,3 +25,5 @@ TEXT ·libc_sysconf_trampoline(SB),NOSPLIT,$0-0; JMP libc_sysconf(SB) TEXT ·libc_faccessat_trampoline(SB),NOSPLIT,$0-0; JMP libc_faccessat(SB) TEXT ·libc_readlinkat_trampoline(SB),NOSPLIT,$0-0; JMP libc_readlinkat(SB) TEXT ·libc_mkdirat_trampoline(SB),NOSPLIT,$0-0; JMP libc_mkdirat(SB) +TEXT ·libc_fchmodat_trampoline(SB),NOSPLIT,$0-0; JMP libc_fchmodat(SB) +TEXT ·libc_fchownat_trampoline(SB),NOSPLIT,$0-0; JMP libc_fchownat(SB) diff --git a/src/internal/syscall/unix/asm_openbsd.s b/src/internal/syscall/unix/asm_openbsd.s index 90f6831e4e53dc..b804a52714c3b4 100644 --- a/src/internal/syscall/unix/asm_openbsd.s +++ b/src/internal/syscall/unix/asm_openbsd.s @@ -14,3 +14,7 @@ TEXT ·libc_readlinkat_trampoline(SB),NOSPLIT,$0-0 JMP libc_readlinkat(SB) TEXT ·libc_mkdirat_trampoline(SB),NOSPLIT,$0-0 JMP libc_mkdirat(SB) +TEXT ·libc_fchmodat_trampoline(SB),NOSPLIT,$0-0 + JMP libc_fchmodat(SB) +TEXT ·libc_fchownat_trampoline(SB),NOSPLIT,$0-0 + JMP libc_fchownat(SB) diff --git a/src/internal/syscall/unix/at.go b/src/internal/syscall/unix/at.go index 27a798e0461d70..794f8ace14f20d 100644 --- a/src/internal/syscall/unix/at.go +++ b/src/internal/syscall/unix/at.go @@ -79,3 +79,38 @@ func Mkdirat(dirfd int, path string, mode uint32) error { } return nil } + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall.Syscall6(fchmodatTrap, + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(mode), + uintptr(flags), + 0, 0) + if errno != 0 { + return errno + } + return nil +} + +func Fchownat(dirfd int, path string, uid, gid int, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall.Syscall6(fchownatTrap, + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(uid), + uintptr(gid), + uintptr(flags), + 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/internal/syscall/unix/at_aix.go b/src/internal/syscall/unix/at_aix.go index 5c2f00efe59cf3..aa188cdb76f6bf 100644 --- a/src/internal/syscall/unix/at_aix.go +++ b/src/internal/syscall/unix/at_aix.go @@ -4,6 +4,8 @@ package unix +//go:cgo_import_dynamic libc_fchmodat fchmodat "libc.a/shr_64.o" +//go:cgo_import_dynamic libc_fchownat fchownat "libc.a/shr_64.o" //go:cgo_import_dynamic libc_fstatat fstatat "libc.a/shr_64.o" //go:cgo_import_dynamic libc_openat openat "libc.a/shr_64.o" //go:cgo_import_dynamic libc_unlinkat unlinkat "libc.a/shr_64.o" diff --git a/src/internal/syscall/unix/at_darwin.go b/src/internal/syscall/unix/at_darwin.go index dbcae5a7889a0a..75d7b455691859 100644 --- a/src/internal/syscall/unix/at_darwin.go +++ b/src/internal/syscall/unix/at_darwin.go @@ -58,3 +58,47 @@ func Mkdirat(dirfd int, path string, mode uint32) error { } return nil } + +func libc_fchmodat_trampoline() + +//go:cgo_import_dynamic libc_fchmodat fchmodat "/usr/lib/libSystem.B.dylib" + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_fchmodat_trampoline), + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(mode), + uintptr(flags), + 0, + 0) + if errno != 0 { + return errno + } + return nil +} + +func libc_fchownat_trampoline() + +//go:cgo_import_dynamic libc_fchownat fchownat "/usr/lib/libSystem.B.dylib" + +func Fchownat(dirfd int, path string, uid, gid int, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_fchownat_trampoline), + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(uid), + uintptr(gid), + uintptr(flags), + 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/internal/syscall/unix/at_fstatat.go b/src/internal/syscall/unix/at_fstatat.go index 25de336a8041c4..18cd62be203556 100644 --- a/src/internal/syscall/unix/at_fstatat.go +++ b/src/internal/syscall/unix/at_fstatat.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build dragonfly || (linux && !loong64) || netbsd || (openbsd && mips64) +//go:build dragonfly || (linux && !(loong64 || mips64 || mips64le)) || netbsd || (openbsd && mips64) package unix diff --git a/src/internal/syscall/unix/at_fstatat2.go b/src/internal/syscall/unix/at_fstatat2.go index 8d20e1a885bd13..b09aecbcdda4dd 100644 --- a/src/internal/syscall/unix/at_fstatat2.go +++ b/src/internal/syscall/unix/at_fstatat2.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build freebsd || (linux && loong64) +//go:build freebsd || (linux && (loong64 || mips64 || mips64le)) package unix diff --git a/src/internal/syscall/unix/at_libc.go b/src/internal/syscall/unix/at_libc.go index faf38be602af65..23db8cf6ac0ca6 100644 --- a/src/internal/syscall/unix/at_libc.go +++ b/src/internal/syscall/unix/at_libc.go @@ -16,13 +16,17 @@ import ( //go:linkname procUnlinkat libc_unlinkat //go:linkname procReadlinkat libc_readlinkat //go:linkname procMkdirat libc_mkdirat +//go:linkname procFchmodat libc_fchmodat +//go:linkname procFchownat libc_fchownat var ( procFstatat, procOpenat, procUnlinkat, procReadlinkat, - procMkdirat uintptr + procMkdirat, + procFchmodat, + procFchownat uintptr ) func Unlinkat(dirfd int, path string, flags int) error { @@ -107,3 +111,38 @@ func Mkdirat(dirfd int, path string, mode uint32) error { } return nil } + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall6(uintptr(unsafe.Pointer(&procFchmodat)), 4, + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(mode), + uintptr(flags), + 0, 0) + if errno != 0 { + return errno + } + return nil +} + +func Fchownat(dirfd int, path string, uid, gid int, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall6(uintptr(unsafe.Pointer(&procFchownat)), 4, + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(uid), + uintptr(gid), + uintptr(flags), + 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/internal/syscall/unix/at_openbsd.go b/src/internal/syscall/unix/at_openbsd.go index 69463e00b94be9..22c959b0c7e1ed 100644 --- a/src/internal/syscall/unix/at_openbsd.go +++ b/src/internal/syscall/unix/at_openbsd.go @@ -49,3 +49,47 @@ func Mkdirat(dirfd int, path string, mode uint32) error { } return nil } + +//go:cgo_import_dynamic libc_fchmodat fchmodat "libc.so" + +func libc_fchmodat_trampoline() + +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_fchmodat_trampoline), + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(mode), + uintptr(flags), + 0, + 0) + if errno != 0 { + return errno + } + return nil +} + +//go:cgo_import_dynamic libc_fchownat fchownat "libc.so" + +func libc_fchownat_trampoline() + +func Fchownat(dirfd int, path string, uid, gid int, flags int) error { + p, err := syscall.BytePtrFromString(path) + if err != nil { + return err + } + _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_fchownat_trampoline), + uintptr(dirfd), + uintptr(unsafe.Pointer(p)), + uintptr(uid), + uintptr(gid), + uintptr(flags), + 0) + if errno != 0 { + return errno + } + return nil +} diff --git a/src/internal/syscall/unix/at_solaris.go b/src/internal/syscall/unix/at_solaris.go index fa65d9e8d95990..f84e8e35daa11c 100644 --- a/src/internal/syscall/unix/at_solaris.go +++ b/src/internal/syscall/unix/at_solaris.go @@ -13,6 +13,8 @@ func syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno) //go:cgo_import_dynamic libc_faccessat faccessat "libc.so" +//go:cgo_import_dynamic libc_fchmodat fchmodat "libc.so" +//go:cgo_import_dynamic libc_fchownat fchownat "libc.so" //go:cgo_import_dynamic libc_fstatat fstatat "libc.so" //go:cgo_import_dynamic libc_openat openat "libc.so" //go:cgo_import_dynamic libc_unlinkat unlinkat "libc.so" diff --git a/src/internal/syscall/unix/at_sysnum_dragonfly.go b/src/internal/syscall/unix/at_sysnum_dragonfly.go index d0ba12a78af055..1e89e97f388077 100644 --- a/src/internal/syscall/unix/at_sysnum_dragonfly.go +++ b/src/internal/syscall/unix/at_sysnum_dragonfly.go @@ -12,6 +12,8 @@ const ( fstatatTrap uintptr = syscall.SYS_FSTATAT readlinkatTrap uintptr = syscall.SYS_READLINKAT mkdiratTrap uintptr = syscall.SYS_MKDIRAT + fchmodatTrap uintptr = syscall.SYS_FCHMODAT + fchownatTrap uintptr = syscall.SYS_FCHOWNAT AT_EACCESS = 0x4 AT_FDCWD = 0xfffafdcd diff --git a/src/internal/syscall/unix/at_sysnum_freebsd.go b/src/internal/syscall/unix/at_sysnum_freebsd.go index 0f347224322653..59a8c2ce5a21c1 100644 --- a/src/internal/syscall/unix/at_sysnum_freebsd.go +++ b/src/internal/syscall/unix/at_sysnum_freebsd.go @@ -19,4 +19,6 @@ const ( posixFallocateTrap uintptr = syscall.SYS_POSIX_FALLOCATE readlinkatTrap uintptr = syscall.SYS_READLINKAT mkdiratTrap uintptr = syscall.SYS_MKDIRAT + fchmodatTrap uintptr = syscall.SYS_FCHMODAT + fchownatTrap uintptr = syscall.SYS_FCHOWNAT ) diff --git a/src/internal/syscall/unix/at_sysnum_linux.go b/src/internal/syscall/unix/at_sysnum_linux.go index 2885c7c68106e9..35cc4307e92f3d 100644 --- a/src/internal/syscall/unix/at_sysnum_linux.go +++ b/src/internal/syscall/unix/at_sysnum_linux.go @@ -11,6 +11,8 @@ const ( openatTrap uintptr = syscall.SYS_OPENAT readlinkatTrap uintptr = syscall.SYS_READLINKAT mkdiratTrap uintptr = syscall.SYS_MKDIRAT + fchmodatTrap uintptr = syscall.SYS_FCHMODAT + fchownatTrap uintptr = syscall.SYS_FCHOWNAT ) const ( diff --git a/src/internal/syscall/unix/at_sysnum_netbsd.go b/src/internal/syscall/unix/at_sysnum_netbsd.go index 820b977436377f..bb946b6581d253 100644 --- a/src/internal/syscall/unix/at_sysnum_netbsd.go +++ b/src/internal/syscall/unix/at_sysnum_netbsd.go @@ -12,6 +12,8 @@ const ( fstatatTrap uintptr = syscall.SYS_FSTATAT readlinkatTrap uintptr = syscall.SYS_READLINKAT mkdiratTrap uintptr = syscall.SYS_MKDIRAT + fchmodatTrap uintptr = syscall.SYS_FCHMODAT + fchownatTrap uintptr = syscall.SYS_FCHOWNAT ) const ( diff --git a/src/internal/syscall/unix/at_wasip1.go b/src/internal/syscall/unix/at_wasip1.go index cd0cb4b7e47350..3fdc95436c2dc1 100644 --- a/src/internal/syscall/unix/at_wasip1.go +++ b/src/internal/syscall/unix/at_wasip1.go @@ -101,6 +101,16 @@ func Mkdirat(dirfd int, path string, mode uint32) error { )) } +func Fchmodat(dirfd int, path string, mode uint32, flags int) error { + // WASI preview 1 doesn't support changing file modes. + return syscall.ENOSYS +} + +func Fchownat(dirfd int, path string, uid, gid int, flags int) error { + // WASI preview 1 doesn't support changing file ownership. + return syscall.ENOSYS +} + //go:wasmimport wasi_snapshot_preview1 path_create_directory //go:noescape func path_create_directory(fd int32, path *byte, pathLen size) syscall.Errno diff --git a/src/internal/syscall/windows/at_windows.go b/src/internal/syscall/windows/at_windows.go index 18429773c060e7..19bcc0dbac10ce 100644 --- a/src/internal/syscall/windows/at_windows.go +++ b/src/internal/syscall/windows/at_windows.go @@ -20,9 +20,10 @@ const ( O_DIRECTORY = 0x100000 // target must be a directory O_NOFOLLOW_ANY = 0x20000000 // disallow symlinks anywhere in the path O_OPEN_REPARSE = 0x40000000 // FILE_OPEN_REPARSE_POINT, used by Lstat + O_WRITE_ATTRS = 0x80000000 // FILE_WRITE_ATTRIBUTES, used by Chmod ) -func Openat(dirfd syscall.Handle, name string, flag int, perm uint32) (_ syscall.Handle, e1 error) { +func Openat(dirfd syscall.Handle, name string, flag uint64, perm uint32) (_ syscall.Handle, e1 error) { if len(name) == 0 { return syscall.InvalidHandle, syscall.ERROR_FILE_NOT_FOUND } @@ -61,6 +62,9 @@ func Openat(dirfd syscall.Handle, name string, flag int, perm uint32) (_ syscall if flag&syscall.O_SYNC != 0 { options |= FILE_WRITE_THROUGH } + if flag&O_WRITE_ATTRS != 0 { + access |= FILE_WRITE_ATTRIBUTES + } // Allow File.Stat. access |= STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | FILE_READ_EA @@ -129,7 +133,7 @@ func Openat(dirfd syscall.Handle, name string, flag int, perm uint32) (_ syscall } // ntCreateFileError maps error returns from NTCreateFile to user-visible errors. -func ntCreateFileError(err error, flag int) error { +func ntCreateFileError(err error, flag uint64) error { s, ok := err.(NTStatus) if !ok { // Shouldn't really be possible, NtCreateFile always returns NTStatus. diff --git a/src/internal/syscall/windows/at_windows_test.go b/src/internal/syscall/windows/at_windows_test.go index 7da9ecf07a2f73..daeb4fcde37d70 100644 --- a/src/internal/syscall/windows/at_windows_test.go +++ b/src/internal/syscall/windows/at_windows_test.go @@ -46,7 +46,7 @@ func TestOpen(t *testing.T) { continue } base := filepath.Base(tt.path) - h, err := windows.Openat(dirfd, base, tt.flag, 0o660) + h, err := windows.Openat(dirfd, base, uint64(tt.flag), 0o660) syscall.CloseHandle(dirfd) if err == nil { syscall.CloseHandle(h) diff --git a/src/internal/trace/base.go b/src/internal/trace/base.go index 4f4ce486305434..693dbc6fa68d1a 100644 --- a/src/internal/trace/base.go +++ b/src/internal/trace/base.go @@ -12,23 +12,18 @@ import ( "math" "strings" - "internal/trace/event" - "internal/trace/event/go122" + "internal/trace/tracev2" "internal/trace/version" ) -// maxArgs is the maximum number of arguments for "plain" events, -// i.e. anything that could reasonably be represented as a baseEvent. -const maxArgs = 5 - // timedEventArgs is an array that is able to hold the arguments for any // timed event. -type timedEventArgs [maxArgs - 1]uint64 +type timedEventArgs [tracev2.MaxTimedEventArgs - 1]uint64 // baseEvent is the basic unprocessed event. This serves as a common // fundamental data structure across. type baseEvent struct { - typ event.Type + typ tracev2.EventType time Time args timedEventArgs } @@ -38,7 +33,7 @@ type baseEvent struct { func (e *baseEvent) extra(v version.Version) []uint64 { switch v { case version.Go122: - return e.args[len(go122.Specs()[e.typ].Args)-1:] + return e.args[len(tracev2.Specs()[e.typ].Args)-1:] } panic(fmt.Sprintf("unsupported version: go 1.%d", v)) } @@ -58,9 +53,8 @@ type evTable struct { extraStringIDs map[string]extraStringID nextExtra extraStringID - // expData contains extra unparsed data that is accessible - // only to ExperimentEvent via an EventExperimental event. - expData map[event.Experiment]*ExperimentalData + // expBatches contains extra unparsed data relevant to a specific experiment. + expBatches map[tracev2.Experiment][]ExperimentalBatch } // addExtraString adds an extra string to the evTable and returns @@ -240,7 +234,7 @@ func (s cpuSample) asEvent(table *evTable) Event { table: table, ctx: s.schedCtx, base: baseEvent{ - typ: go122.EvCPUSample, + typ: tracev2.EvCPUSample, time: s.time, }, } diff --git a/src/internal/trace/batch.go b/src/internal/trace/batch.go index 2b47c9b72cb8d9..58f18d63819c53 100644 --- a/src/internal/trace/batch.go +++ b/src/internal/trace/batch.go @@ -10,8 +10,7 @@ import ( "fmt" "io" - "internal/trace/event" - "internal/trace/event/go122" + "internal/trace/tracev2" ) // timestamp is an unprocessed timestamp. @@ -23,23 +22,23 @@ type batch struct { m ThreadID time timestamp data []byte - exp event.Experiment + exp tracev2.Experiment } func (b *batch) isStringsBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStrings + return b.exp == tracev2.NoExperiment && len(b.data) > 0 && tracev2.EventType(b.data[0]) == tracev2.EvStrings } func (b *batch) isStacksBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvStacks + return b.exp == tracev2.NoExperiment && len(b.data) > 0 && tracev2.EventType(b.data[0]) == tracev2.EvStacks } func (b *batch) isCPUSamplesBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvCPUSamples + return b.exp == tracev2.NoExperiment && len(b.data) > 0 && tracev2.EventType(b.data[0]) == tracev2.EvCPUSamples } func (b *batch) isFreqBatch() bool { - return b.exp == event.NoExperiment && len(b.data) > 0 && event.Type(b.data[0]) == go122.EvFrequency + return b.exp == tracev2.NoExperiment && len(b.data) > 0 && tracev2.EventType(b.data[0]) == tracev2.EvFrequency } // readBatch reads the next full batch from r. @@ -52,18 +51,18 @@ func readBatch(r interface { if err != nil { return batch{}, 0, err } - if typ := event.Type(b); typ != go122.EvEventBatch && typ != go122.EvExperimentalBatch { - return batch{}, 0, fmt.Errorf("expected batch event, got %s", go122.EventString(typ)) + if typ := tracev2.EventType(b); typ != tracev2.EvEventBatch && typ != tracev2.EvExperimentalBatch { + return batch{}, 0, fmt.Errorf("expected batch event, got event %d", typ) } // Read the experiment of we have one. - exp := event.NoExperiment - if event.Type(b) == go122.EvExperimentalBatch { + exp := tracev2.NoExperiment + if tracev2.EventType(b) == tracev2.EvExperimentalBatch { e, err := r.ReadByte() if err != nil { return batch{}, 0, err } - exp = event.Experiment(e) + exp = tracev2.Experiment(e) } // Read the batch header: gen (generation), thread (M) ID, base timestamp @@ -86,8 +85,8 @@ func readBatch(r interface { if err != nil { return batch{}, gen, fmt.Errorf("error reading batch size: %w", err) } - if size > go122.MaxBatchSize { - return batch{}, gen, fmt.Errorf("invalid batch size %d, maximum is %d", size, go122.MaxBatchSize) + if size > tracev2.MaxBatchSize { + return batch{}, gen, fmt.Errorf("invalid batch size %d, maximum is %d", size, tracev2.MaxBatchSize) } // Copy out the batch for later processing. diff --git a/src/internal/trace/batchcursor.go b/src/internal/trace/batchcursor.go index 66d297ee33a486..8582f30bb06e88 100644 --- a/src/internal/trace/batchcursor.go +++ b/src/internal/trace/batchcursor.go @@ -9,8 +9,7 @@ import ( "encoding/binary" "fmt" - "internal/trace/event" - "internal/trace/event/go122" + "internal/trace/tracev2" ) type batchCursor struct { @@ -66,8 +65,8 @@ func (b *batchCursor) compare(a *batchCursor) int { // be the case for every event in a plain EventBatch. func readTimedBaseEvent(b []byte, e *baseEvent) (int, timestamp, error) { // Get the event type. - typ := event.Type(b[0]) - specs := go122.Specs() + typ := tracev2.EventType(b[0]) + specs := tracev2.Specs() if int(typ) >= len(specs) { return 0, 0, fmt.Errorf("found invalid event type: %v", typ) } diff --git a/src/internal/trace/event.go b/src/internal/trace/event.go index a5c5aec2f8a8ba..896ab7f73a1f1c 100644 --- a/src/internal/trace/event.go +++ b/src/internal/trace/event.go @@ -11,8 +11,7 @@ import ( "strings" "time" - "internal/trace/event" - "internal/trace/event/go122" + "internal/trace/tracev2" "internal/trace/version" ) @@ -313,26 +312,28 @@ type ExperimentalEvent struct { // Name is the name of the event. Name string - // ArgNames is the names of the event's arguments in order. - // This may refer to a globally shared slice. Copy before mutating. - ArgNames []string + // Experiment is the name of the experiment this event is a part of. + Experiment string - // Args contains the event's arguments. - Args []uint64 + // Args lists the names of the event's arguments in order. + Args []string - // Data is additional unparsed data that is associated with the experimental event. - // Data is likely to be shared across many ExperimentalEvents, so callers that parse - // Data are encouraged to cache the parse result and look it up by the value of Data. - Data *ExperimentalData + // argValues contains the raw integer arguments which are interpreted + // by ArgValue using table. + table *evTable + argValues []uint64 } -// ExperimentalData represents some raw and unparsed sidecar data present in the trace that is -// associated with certain kinds of experimental events. For example, this data may contain -// tables needed to interpret ExperimentalEvent arguments, or the ExperimentEvent could just be -// a placeholder for a differently encoded event that's actually present in the experimental data. -type ExperimentalData struct { - // Batches contain the actual experimental data, along with metadata about each batch. - Batches []ExperimentalBatch +// ArgValue returns a typed Value for the i'th argument in the experimental event. +func (e ExperimentalEvent) ArgValue(i int) Value { + if i < 0 || i >= len(e.Args) { + panic(fmt.Sprintf("experimental event argument index %d out of bounds [0, %d)", i, len(e.Args))) + } + if strings.HasSuffix(e.Args[i], "string") { + s := e.table.strings.mustGet(stringID(e.argValues[i])) + return stringValue(s) + } + return uint64Value(e.argValues[i]) } // ExperimentalBatch represents a packet of unparsed data along with metadata about that packet. @@ -353,7 +354,7 @@ type Event struct { // Kind returns the kind of event that this is. func (e Event) Kind() EventKind { - return go122Type2Kind[e.base.typ] + return tracev2Type2Kind[e.base.typ] } // Time returns the timestamp of the event. @@ -405,10 +406,10 @@ func (e Event) Stack() Stack { if e.base.typ == evSync { return NoStack } - if e.base.typ == go122.EvCPUSample { + if e.base.typ == tracev2.EvCPUSample { return Stack{table: e.table, id: stackID(e.base.args[0])} } - spec := go122.Specs()[e.base.typ] + spec := tracev2.Specs()[e.base.typ] if len(spec.StackIDs) == 0 { return NoStack } @@ -431,17 +432,17 @@ func (e Event) Metric() Metric { } var m Metric switch e.base.typ { - case go122.EvProcsChange: + case tracev2.EvProcsChange: m.Name = "/sched/gomaxprocs:threads" - m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} - case go122.EvHeapAlloc: + m.Value = uint64Value(e.base.args[0]) + case tracev2.EvHeapAlloc: m.Name = "/memory/classes/heap/objects:bytes" - m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} - case go122.EvHeapGoal: + m.Value = uint64Value(e.base.args[0]) + case tracev2.EvHeapGoal: m.Name = "/gc/heap/goal:bytes" - m.Value = Value{kind: ValueUint64, scalar: e.base.args[0]} + m.Value = uint64Value(e.base.args[0]) default: - panic(fmt.Sprintf("internal error: unexpected event type for Metric kind: %s", go122.EventString(e.base.typ))) + panic(fmt.Sprintf("internal error: unexpected wire-format event type for Metric kind: %d", e.base.typ)) } return m } @@ -453,8 +454,8 @@ func (e Event) Label() Label { if e.Kind() != EventLabel { panic("Label called on non-Label event") } - if e.base.typ != go122.EvGoLabel { - panic(fmt.Sprintf("internal error: unexpected event type for Label kind: %s", go122.EventString(e.base.typ))) + if e.base.typ != tracev2.EvGoLabel { + panic(fmt.Sprintf("internal error: unexpected wire-format event type for Label kind: %d", e.base.typ)) } return Label{ Label: e.table.strings.mustGet(stringID(e.base.args[0])), @@ -471,33 +472,33 @@ func (e Event) Range() Range { } var r Range switch e.base.typ { - case go122.EvSTWBegin, go122.EvSTWEnd: + case tracev2.EvSTWBegin, tracev2.EvSTWEnd: // N.B. ordering.advance smuggles in the STW reason as e.base.args[0] - // for go122.EvSTWEnd (it's already there for Begin). + // for tracev2.EvSTWEnd (it's already there for Begin). r.Name = "stop-the-world (" + e.table.strings.mustGet(stringID(e.base.args[0])) + ")" r.Scope = ResourceID{Kind: ResourceGoroutine, id: int64(e.Goroutine())} - case go122.EvGCBegin, go122.EvGCActive, go122.EvGCEnd: + case tracev2.EvGCBegin, tracev2.EvGCActive, tracev2.EvGCEnd: r.Name = "GC concurrent mark phase" r.Scope = ResourceID{Kind: ResourceNone} - case go122.EvGCSweepBegin, go122.EvGCSweepActive, go122.EvGCSweepEnd: + case tracev2.EvGCSweepBegin, tracev2.EvGCSweepActive, tracev2.EvGCSweepEnd: r.Name = "GC incremental sweep" r.Scope = ResourceID{Kind: ResourceProc} - if e.base.typ == go122.EvGCSweepActive { + if e.base.typ == tracev2.EvGCSweepActive { r.Scope.id = int64(e.base.args[0]) } else { r.Scope.id = int64(e.Proc()) } r.Scope.id = int64(e.Proc()) - case go122.EvGCMarkAssistBegin, go122.EvGCMarkAssistActive, go122.EvGCMarkAssistEnd: + case tracev2.EvGCMarkAssistBegin, tracev2.EvGCMarkAssistActive, tracev2.EvGCMarkAssistEnd: r.Name = "GC mark assist" r.Scope = ResourceID{Kind: ResourceGoroutine} - if e.base.typ == go122.EvGCMarkAssistActive { + if e.base.typ == tracev2.EvGCMarkAssistActive { r.Scope.id = int64(e.base.args[0]) } else { r.Scope.id = int64(e.Goroutine()) } default: - panic(fmt.Sprintf("internal error: unexpected event type for Range kind: %s", go122.EventString(e.base.typ))) + panic(fmt.Sprintf("internal error: unexpected wire-event type for Range kind: %d", e.base.typ)) } return r } @@ -509,17 +510,17 @@ func (e Event) RangeAttributes() []RangeAttribute { if e.Kind() != EventRangeEnd { panic("Range called on non-Range event") } - if e.base.typ != go122.EvGCSweepEnd { + if e.base.typ != tracev2.EvGCSweepEnd { return nil } return []RangeAttribute{ { Name: "bytes swept", - Value: Value{kind: ValueUint64, scalar: e.base.args[0]}, + Value: uint64Value(e.base.args[0]), }, { Name: "bytes reclaimed", - Value: Value{kind: ValueUint64, scalar: e.base.args[1]}, + Value: uint64Value(e.base.args[1]), }, } } @@ -534,14 +535,14 @@ func (e Event) Task() Task { parentID := NoTask var typ string switch e.base.typ { - case go122.EvUserTaskBegin: + case tracev2.EvUserTaskBegin: parentID = TaskID(e.base.args[1]) typ = e.table.strings.mustGet(stringID(e.base.args[2])) - case go122.EvUserTaskEnd: + case tracev2.EvUserTaskEnd: parentID = TaskID(e.base.extra(version.Go122)[0]) typ = e.table.getExtraString(extraStringID(e.base.extra(version.Go122)[1])) default: - panic(fmt.Sprintf("internal error: unexpected event type for Task kind: %s", go122.EventString(e.base.typ))) + panic(fmt.Sprintf("internal error: unexpected wire-format event type for Task kind: %d", e.base.typ)) } return Task{ ID: TaskID(e.base.args[0]), @@ -557,8 +558,8 @@ func (e Event) Region() Region { if kind := e.Kind(); kind != EventRegionBegin && kind != EventRegionEnd { panic("Region called on non-Region event") } - if e.base.typ != go122.EvUserRegionBegin && e.base.typ != go122.EvUserRegionEnd { - panic(fmt.Sprintf("internal error: unexpected event type for Region kind: %s", go122.EventString(e.base.typ))) + if e.base.typ != tracev2.EvUserRegionBegin && e.base.typ != tracev2.EvUserRegionEnd { + panic(fmt.Sprintf("internal error: unexpected wire-format event type for Region kind: %d", e.base.typ)) } return Region{ Task: TaskID(e.base.args[0]), @@ -573,8 +574,8 @@ func (e Event) Log() Log { if e.Kind() != EventLog { panic("Log called on non-Log event") } - if e.base.typ != go122.EvUserLog { - panic(fmt.Sprintf("internal error: unexpected event type for Log kind: %s", go122.EventString(e.base.typ))) + if e.base.typ != tracev2.EvUserLog { + panic(fmt.Sprintf("internal error: unexpected wire-format event type for Log kind: %d", e.base.typ)) } return Log{ Task: TaskID(e.base.args[0]), @@ -592,14 +593,14 @@ func (e Event) StateTransition() StateTransition { } var s StateTransition switch e.base.typ { - case go122.EvProcStart: + case tracev2.EvProcStart: s = procStateTransition(ProcID(e.base.args[0]), ProcIdle, ProcRunning) - case go122.EvProcStop: + case tracev2.EvProcStop: s = procStateTransition(e.ctx.P, ProcRunning, ProcIdle) - case go122.EvProcSteal: + case tracev2.EvProcSteal: // N.B. ordering.advance populates e.base.extra. beforeState := ProcRunning - if go122.ProcStatus(e.base.extra(version.Go122)[0]) == go122.ProcSyscallAbandoned { + if tracev2.ProcStatus(e.base.extra(version.Go122)[0]) == tracev2.ProcSyscallAbandoned { // We've lost information because this ProcSteal advanced on a // SyscallAbandoned state. Treat the P as idle because ProcStatus // treats SyscallAbandoned as Idle. Otherwise we'll have an invalid @@ -607,57 +608,86 @@ func (e Event) StateTransition() StateTransition { beforeState = ProcIdle } s = procStateTransition(ProcID(e.base.args[0]), beforeState, ProcIdle) - case go122.EvProcStatus: + case tracev2.EvProcStatus: // N.B. ordering.advance populates e.base.extra. - s = procStateTransition(ProcID(e.base.args[0]), ProcState(e.base.extra(version.Go122)[0]), go122ProcStatus2ProcState[e.base.args[1]]) - case go122.EvGoCreate, go122.EvGoCreateBlocked: + s = procStateTransition(ProcID(e.base.args[0]), ProcState(e.base.extra(version.Go122)[0]), tracev2ProcStatus2ProcState[e.base.args[1]]) + case tracev2.EvGoCreate, tracev2.EvGoCreateBlocked: status := GoRunnable - if e.base.typ == go122.EvGoCreateBlocked { + if e.base.typ == tracev2.EvGoCreateBlocked { status = GoWaiting } s = goStateTransition(GoID(e.base.args[0]), GoNotExist, status) s.Stack = Stack{table: e.table, id: stackID(e.base.args[1])} - case go122.EvGoCreateSyscall: + case tracev2.EvGoCreateSyscall: s = goStateTransition(GoID(e.base.args[0]), GoNotExist, GoSyscall) - case go122.EvGoStart: + case tracev2.EvGoStart: s = goStateTransition(GoID(e.base.args[0]), GoRunnable, GoRunning) - case go122.EvGoDestroy: + case tracev2.EvGoDestroy: s = goStateTransition(e.ctx.G, GoRunning, GoNotExist) s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoDestroySyscall: + case tracev2.EvGoDestroySyscall: s = goStateTransition(e.ctx.G, GoSyscall, GoNotExist) - case go122.EvGoStop: + case tracev2.EvGoStop: s = goStateTransition(e.ctx.G, GoRunning, GoRunnable) s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoBlock: + case tracev2.EvGoBlock: s = goStateTransition(e.ctx.G, GoRunning, GoWaiting) s.Reason = e.table.strings.mustGet(stringID(e.base.args[0])) s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoUnblock, go122.EvGoSwitch, go122.EvGoSwitchDestroy: + case tracev2.EvGoUnblock, tracev2.EvGoSwitch, tracev2.EvGoSwitchDestroy: // N.B. GoSwitch and GoSwitchDestroy both emit additional events, but // the first thing they both do is unblock the goroutine they name, // identically to an unblock event (even their arguments match). s = goStateTransition(GoID(e.base.args[0]), GoWaiting, GoRunnable) - case go122.EvGoSyscallBegin: + case tracev2.EvGoSyscallBegin: s = goStateTransition(e.ctx.G, GoRunning, GoSyscall) s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoSyscallEnd: + case tracev2.EvGoSyscallEnd: s = goStateTransition(e.ctx.G, GoSyscall, GoRunning) s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoSyscallEndBlocked: + case tracev2.EvGoSyscallEndBlocked: s = goStateTransition(e.ctx.G, GoSyscall, GoRunnable) s.Stack = e.Stack() // This event references the resource the event happened on. - case go122.EvGoStatus, go122.EvGoStatusStack: + case tracev2.EvGoStatus, tracev2.EvGoStatusStack: packedStatus := e.base.args[2] from, to := packedStatus>>32, packedStatus&((1<<32)-1) - s = goStateTransition(GoID(e.base.args[0]), GoState(from), go122GoStatus2GoState[to]) + s = goStateTransition(GoID(e.base.args[0]), GoState(from), tracev2GoStatus2GoState[to]) default: - panic(fmt.Sprintf("internal error: unexpected event type for StateTransition kind: %s", go122.EventString(e.base.typ))) + panic(fmt.Sprintf("internal error: unexpected wire-format event type for StateTransition kind: %d", e.base.typ)) } return s } +// Sync returns details that are relevant for the following events, up to but excluding the +// next EventSync event. +func (e Event) Sync() Sync { + if e.Kind() != EventSync { + panic("Sync called on non-Sync event") + } + var expBatches map[string][]ExperimentalBatch + if e.table != nil { + expBatches = make(map[string][]ExperimentalBatch) + for exp, batches := range e.table.expBatches { + expBatches[tracev2.Experiments()[exp]] = batches + } + } + return Sync{ + N: int(e.base.args[0]), + ExperimentalBatches: expBatches, + } +} + +// Sync contains details potentially relevant to all the following events, up to but excluding +// the next EventSync event. +type Sync struct { + // N indicates that this is the Nth sync event in the trace. + N int + + // ExperimentalBatches contain all the unparsed batches of data for a given experiment. + ExperimentalBatches map[string][]ExperimentalBatch +} + // Experimental returns a view of the raw event for an experimental event. // // Panics if Kind != EventExperimental. @@ -665,84 +695,85 @@ func (e Event) Experimental() ExperimentalEvent { if e.Kind() != EventExperimental { panic("Experimental called on non-Experimental event") } - spec := go122.Specs()[e.base.typ] + spec := tracev2.Specs()[e.base.typ] argNames := spec.Args[1:] // Skip timestamp; already handled. return ExperimentalEvent{ - Name: spec.Name, - ArgNames: argNames, - Args: e.base.args[:len(argNames)], - Data: e.table.expData[spec.Experiment], - } -} - -const evSync = ^event.Type(0) - -var go122Type2Kind = [...]EventKind{ - go122.EvCPUSample: EventStackSample, - go122.EvProcsChange: EventMetric, - go122.EvProcStart: EventStateTransition, - go122.EvProcStop: EventStateTransition, - go122.EvProcSteal: EventStateTransition, - go122.EvProcStatus: EventStateTransition, - go122.EvGoCreate: EventStateTransition, - go122.EvGoCreateSyscall: EventStateTransition, - go122.EvGoStart: EventStateTransition, - go122.EvGoDestroy: EventStateTransition, - go122.EvGoDestroySyscall: EventStateTransition, - go122.EvGoStop: EventStateTransition, - go122.EvGoBlock: EventStateTransition, - go122.EvGoUnblock: EventStateTransition, - go122.EvGoSyscallBegin: EventStateTransition, - go122.EvGoSyscallEnd: EventStateTransition, - go122.EvGoSyscallEndBlocked: EventStateTransition, - go122.EvGoStatus: EventStateTransition, - go122.EvSTWBegin: EventRangeBegin, - go122.EvSTWEnd: EventRangeEnd, - go122.EvGCActive: EventRangeActive, - go122.EvGCBegin: EventRangeBegin, - go122.EvGCEnd: EventRangeEnd, - go122.EvGCSweepActive: EventRangeActive, - go122.EvGCSweepBegin: EventRangeBegin, - go122.EvGCSweepEnd: EventRangeEnd, - go122.EvGCMarkAssistActive: EventRangeActive, - go122.EvGCMarkAssistBegin: EventRangeBegin, - go122.EvGCMarkAssistEnd: EventRangeEnd, - go122.EvHeapAlloc: EventMetric, - go122.EvHeapGoal: EventMetric, - go122.EvGoLabel: EventLabel, - go122.EvUserTaskBegin: EventTaskBegin, - go122.EvUserTaskEnd: EventTaskEnd, - go122.EvUserRegionBegin: EventRegionBegin, - go122.EvUserRegionEnd: EventRegionEnd, - go122.EvUserLog: EventLog, - go122.EvGoSwitch: EventStateTransition, - go122.EvGoSwitchDestroy: EventStateTransition, - go122.EvGoCreateBlocked: EventStateTransition, - go122.EvGoStatusStack: EventStateTransition, - go122.EvSpan: EventExperimental, - go122.EvSpanAlloc: EventExperimental, - go122.EvSpanFree: EventExperimental, - go122.EvHeapObject: EventExperimental, - go122.EvHeapObjectAlloc: EventExperimental, - go122.EvHeapObjectFree: EventExperimental, - go122.EvGoroutineStack: EventExperimental, - go122.EvGoroutineStackAlloc: EventExperimental, - go122.EvGoroutineStackFree: EventExperimental, - evSync: EventSync, -} - -var go122GoStatus2GoState = [...]GoState{ - go122.GoRunnable: GoRunnable, - go122.GoRunning: GoRunning, - go122.GoWaiting: GoWaiting, - go122.GoSyscall: GoSyscall, -} - -var go122ProcStatus2ProcState = [...]ProcState{ - go122.ProcRunning: ProcRunning, - go122.ProcIdle: ProcIdle, - go122.ProcSyscall: ProcRunning, - go122.ProcSyscallAbandoned: ProcIdle, + Name: spec.Name, + Experiment: tracev2.Experiments()[spec.Experiment], + Args: argNames, + table: e.table, + argValues: e.base.args[:len(argNames)], + } +} + +const evSync = ^tracev2.EventType(0) + +var tracev2Type2Kind = [...]EventKind{ + tracev2.EvCPUSample: EventStackSample, + tracev2.EvProcsChange: EventMetric, + tracev2.EvProcStart: EventStateTransition, + tracev2.EvProcStop: EventStateTransition, + tracev2.EvProcSteal: EventStateTransition, + tracev2.EvProcStatus: EventStateTransition, + tracev2.EvGoCreate: EventStateTransition, + tracev2.EvGoCreateSyscall: EventStateTransition, + tracev2.EvGoStart: EventStateTransition, + tracev2.EvGoDestroy: EventStateTransition, + tracev2.EvGoDestroySyscall: EventStateTransition, + tracev2.EvGoStop: EventStateTransition, + tracev2.EvGoBlock: EventStateTransition, + tracev2.EvGoUnblock: EventStateTransition, + tracev2.EvGoSyscallBegin: EventStateTransition, + tracev2.EvGoSyscallEnd: EventStateTransition, + tracev2.EvGoSyscallEndBlocked: EventStateTransition, + tracev2.EvGoStatus: EventStateTransition, + tracev2.EvSTWBegin: EventRangeBegin, + tracev2.EvSTWEnd: EventRangeEnd, + tracev2.EvGCActive: EventRangeActive, + tracev2.EvGCBegin: EventRangeBegin, + tracev2.EvGCEnd: EventRangeEnd, + tracev2.EvGCSweepActive: EventRangeActive, + tracev2.EvGCSweepBegin: EventRangeBegin, + tracev2.EvGCSweepEnd: EventRangeEnd, + tracev2.EvGCMarkAssistActive: EventRangeActive, + tracev2.EvGCMarkAssistBegin: EventRangeBegin, + tracev2.EvGCMarkAssistEnd: EventRangeEnd, + tracev2.EvHeapAlloc: EventMetric, + tracev2.EvHeapGoal: EventMetric, + tracev2.EvGoLabel: EventLabel, + tracev2.EvUserTaskBegin: EventTaskBegin, + tracev2.EvUserTaskEnd: EventTaskEnd, + tracev2.EvUserRegionBegin: EventRegionBegin, + tracev2.EvUserRegionEnd: EventRegionEnd, + tracev2.EvUserLog: EventLog, + tracev2.EvGoSwitch: EventStateTransition, + tracev2.EvGoSwitchDestroy: EventStateTransition, + tracev2.EvGoCreateBlocked: EventStateTransition, + tracev2.EvGoStatusStack: EventStateTransition, + tracev2.EvSpan: EventExperimental, + tracev2.EvSpanAlloc: EventExperimental, + tracev2.EvSpanFree: EventExperimental, + tracev2.EvHeapObject: EventExperimental, + tracev2.EvHeapObjectAlloc: EventExperimental, + tracev2.EvHeapObjectFree: EventExperimental, + tracev2.EvGoroutineStack: EventExperimental, + tracev2.EvGoroutineStackAlloc: EventExperimental, + tracev2.EvGoroutineStackFree: EventExperimental, + evSync: EventSync, +} + +var tracev2GoStatus2GoState = [...]GoState{ + tracev2.GoRunnable: GoRunnable, + tracev2.GoRunning: GoRunning, + tracev2.GoWaiting: GoWaiting, + tracev2.GoSyscall: GoSyscall, +} + +var tracev2ProcStatus2ProcState = [...]ProcState{ + tracev2.ProcRunning: ProcRunning, + tracev2.ProcIdle: ProcIdle, + tracev2.ProcSyscall: ProcRunning, + tracev2.ProcSyscallAbandoned: ProcIdle, } // String returns the event as a human-readable string. @@ -756,7 +787,7 @@ func (e Event) String() string { switch kind := e.Kind(); kind { case EventMetric: m := e.Metric() - fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, valueAsString(m.Value)) + fmt.Fprintf(&sb, " Name=%q Value=%s", m.Name, m.Value) case EventLabel: l := e.Label() fmt.Fprintf(&sb, " Label=%q Resource=%s", l.Label, l.Resource) @@ -769,7 +800,7 @@ func (e Event) String() string { if i != 0 { fmt.Fprintf(&sb, " ") } - fmt.Fprintf(&sb, "%q=%s", attr.Name, valueAsString(attr.Value)) + fmt.Fprintf(&sb, "%q=%s", attr.Name, attr.Value) } fmt.Fprintf(&sb, "]") } @@ -805,7 +836,14 @@ func (e Event) String() string { } case EventExperimental: r := e.Experimental() - fmt.Fprintf(&sb, " Name=%s ArgNames=%v Args=%v", r.Name, r.ArgNames, r.Args) + fmt.Fprintf(&sb, " Name=%s Args=[", r.Name) + for i, arg := range r.Args { + if i != 0 { + fmt.Fprintf(&sb, ", ") + } + fmt.Fprintf(&sb, "%s=%s", arg, r.ArgValue(i).String()) + } + fmt.Fprintf(&sb, "]") } if stk := e.Stack(); stk != NoStack { fmt.Fprintln(&sb) @@ -824,7 +862,7 @@ func (e Event) validateTableIDs() error { if e.base.typ == evSync { return nil } - spec := go122.Specs()[e.base.typ] + spec := tracev2.Specs()[e.base.typ] // Check stacks. for _, i := range spec.StackIDs { @@ -848,8 +886,8 @@ func (e Event) validateTableIDs() error { return nil } -func syncEvent(table *evTable, ts Time) Event { - return Event{ +func syncEvent(table *evTable, ts Time, n int) Event { + ev := Event{ table: table, ctx: schedCtx{ G: NoGoroutine, @@ -861,4 +899,6 @@ func syncEvent(table *evTable, ts Time) Event { time: ts, }, } + ev.base.args[0] = uint64(n) + return ev } diff --git a/src/internal/trace/event/requirements.go b/src/internal/trace/event/requirements.go deleted file mode 100644 index c5adf2e0c239d1..00000000000000 --- a/src/internal/trace/event/requirements.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2023 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. - -package event - -// SchedReqs is a set of constraints on what the scheduling -// context must look like. -type SchedReqs struct { - Thread Constraint - Proc Constraint - Goroutine Constraint -} - -// Constraint represents a various presence requirements. -type Constraint uint8 - -const ( - MustNotHave Constraint = iota - MayHave - MustHave -) - -// UserGoReqs is a common requirement among events that are running -// or are close to running user code. -var UserGoReqs = SchedReqs{Thread: MustHave, Proc: MustHave, Goroutine: MustHave} diff --git a/src/internal/trace/event_test.go b/src/internal/trace/event_test.go index c81a45185dc475..d39d6b75bd7613 100644 --- a/src/internal/trace/event_test.go +++ b/src/internal/trace/event_test.go @@ -8,7 +8,7 @@ import "testing" func TestPanicEvent(t *testing.T) { // Use a sync event for this because it doesn't have any extra metadata. - ev := syncEvent(nil, 0) + ev := syncEvent(nil, 0, 0) mustPanic(t, func() { _ = ev.Range() diff --git a/src/internal/trace/gc.go b/src/internal/trace/gc.go index bf271ed73baf20..f5e8fe79f293be 100644 --- a/src/internal/trace/gc.go +++ b/src/internal/trace/gc.go @@ -75,7 +75,7 @@ func MutatorUtilizationV2(events []Event, flags UtilFlags) [][]MutatorUtil { states := make(map[GoID]GoState) bgMark := make(map[GoID]bool) procs := []procsCount{} - seenSync := false + nSync := 0 // Helpers. handleSTW := func(r Range) bool { @@ -97,13 +97,13 @@ func MutatorUtilizationV2(events []Event, flags UtilFlags) [][]MutatorUtil { // Process the event. switch ev.Kind() { case EventSync: - seenSync = true + nSync = ev.Sync().N case EventMetric: m := ev.Metric() if m.Name != "/sched/gomaxprocs:threads" { break } - gomaxprocs := int(m.Value.Uint64()) + gomaxprocs := int(m.Value.ToUint64()) if len(ps) > gomaxprocs { if flags&UtilPerProc != 0 { // End each P's series. @@ -135,9 +135,9 @@ func MutatorUtilizationV2(events []Event, flags UtilFlags) [][]MutatorUtil { switch ev.Kind() { case EventRangeActive: - if seenSync { - // If we've seen a sync, then we can be sure we're not finding out about - // something late; we have complete information after that point, and these + if nSync > 1 { + // If we've seen a full generation, then we can be sure we're not finding out + // about something late; we have complete information after that point, and these // active events will just be redundant. break } diff --git a/src/internal/trace/generation.go b/src/internal/trace/generation.go index 98bbf4398569f9..90a7f3b6c6bd0e 100644 --- a/src/internal/trace/generation.go +++ b/src/internal/trace/generation.go @@ -14,8 +14,7 @@ import ( "slices" "strings" - "internal/trace/event" - "internal/trace/event/go122" + "internal/trace/tracev2" ) // generation contains all the trace data for a single @@ -27,6 +26,7 @@ type generation struct { batches map[ThreadID][]batch batchMs []ThreadID cpuSamples []cpuSample + minTs timestamp *evTable } @@ -100,6 +100,9 @@ func readGeneration(r *bufio.Reader, spill *spilledBatch) (*generation, *spilled // problem as soon as we see it. return nil, nil, fmt.Errorf("generations out of order") } + if g.minTs == 0 || b.time < g.minTs { + g.minTs = b.time + } if err := processBatch(g, b); err != nil { return nil, nil, err } @@ -162,11 +165,11 @@ func processBatch(g *generation, b batch) error { return fmt.Errorf("found multiple frequency events") } g.freq = freq - case b.exp != event.NoExperiment: - if g.expData == nil { - g.expData = make(map[event.Experiment]*ExperimentalData) + case b.exp != tracev2.NoExperiment: + if g.expBatches == nil { + g.expBatches = make(map[tracev2.Experiment][]ExperimentalBatch) } - if err := addExperimentalData(g.expData, b); err != nil { + if err := addExperimentalBatch(g.expBatches, b); err != nil { return err } default: @@ -218,7 +221,7 @@ func addStrings(stringTable *dataTable[stringID, string], b batch) error { } r := bytes.NewReader(b.data) hdr, err := r.ReadByte() // Consume the EvStrings byte. - if err != nil || event.Type(hdr) != go122.EvStrings { + if err != nil || tracev2.EventType(hdr) != tracev2.EvStrings { return fmt.Errorf("missing strings batch header") } @@ -229,7 +232,7 @@ func addStrings(stringTable *dataTable[stringID, string], b batch) error { if err != nil { return err } - if event.Type(ev) != go122.EvString { + if tracev2.EventType(ev) != tracev2.EvString { return fmt.Errorf("expected string event, got %d", ev) } @@ -244,8 +247,8 @@ func addStrings(stringTable *dataTable[stringID, string], b batch) error { if err != nil { return err } - if len > go122.MaxStringSize { - return fmt.Errorf("invalid string size %d, maximum is %d", len, go122.MaxStringSize) + if len > tracev2.MaxEventTrailerDataSize { + return fmt.Errorf("invalid string size %d, maximum is %d", len, tracev2.MaxEventTrailerDataSize) } // Copy out the string. @@ -276,7 +279,7 @@ func addStacks(stackTable *dataTable[stackID, stack], pcs map[uint64]frame, b ba } r := bytes.NewReader(b.data) hdr, err := r.ReadByte() // Consume the EvStacks byte. - if err != nil || event.Type(hdr) != go122.EvStacks { + if err != nil || tracev2.EventType(hdr) != tracev2.EvStacks { return fmt.Errorf("missing stacks batch header") } @@ -286,7 +289,7 @@ func addStacks(stackTable *dataTable[stackID, stack], pcs map[uint64]frame, b ba if err != nil { return err } - if event.Type(ev) != go122.EvStack { + if tracev2.EventType(ev) != tracev2.EvStack { return fmt.Errorf("expected stack event, got %d", ev) } @@ -301,8 +304,8 @@ func addStacks(stackTable *dataTable[stackID, stack], pcs map[uint64]frame, b ba if err != nil { return err } - if nFrames > go122.MaxFramesPerStack { - return fmt.Errorf("invalid stack size %d, maximum is %d", nFrames, go122.MaxFramesPerStack) + if nFrames > tracev2.MaxFramesPerStack { + return fmt.Errorf("invalid stack size %d, maximum is %d", nFrames, tracev2.MaxFramesPerStack) } // Each frame consists of 4 fields: pc, funcID (string), fileID (string), line. @@ -354,7 +357,7 @@ func addCPUSamples(samples []cpuSample, b batch) ([]cpuSample, error) { } r := bytes.NewReader(b.data) hdr, err := r.ReadByte() // Consume the EvCPUSamples byte. - if err != nil || event.Type(hdr) != go122.EvCPUSamples { + if err != nil || tracev2.EventType(hdr) != tracev2.EvCPUSamples { return nil, fmt.Errorf("missing CPU samples batch header") } @@ -364,7 +367,7 @@ func addCPUSamples(samples []cpuSample, b batch) ([]cpuSample, error) { if err != nil { return nil, err } - if event.Type(ev) != go122.EvCPUSample { + if tracev2.EventType(ev) != tracev2.EvCPUSample { return nil, fmt.Errorf("expected CPU sample event, got %d", ev) } @@ -435,18 +438,13 @@ func parseFreq(b batch) (frequency, error) { return frequency(1.0 / (float64(f) / 1e9)), nil } -// addExperimentalData takes an experimental batch and adds it to the ExperimentalData -// for the experiment its a part of. -func addExperimentalData(expData map[event.Experiment]*ExperimentalData, b batch) error { - if b.exp == event.NoExperiment { - return fmt.Errorf("internal error: addExperimentalData called on non-experimental batch") - } - ed, ok := expData[b.exp] - if !ok { - ed = new(ExperimentalData) - expData[b.exp] = ed +// addExperimentalBatch takes an experimental batch and adds it to the list of experimental +// batches for the experiment its a part of. +func addExperimentalBatch(expBatches map[tracev2.Experiment][]ExperimentalBatch, b batch) error { + if b.exp == tracev2.NoExperiment { + return fmt.Errorf("internal error: addExperimentalBatch called on non-experimental batch") } - ed.Batches = append(ed.Batches, ExperimentalBatch{ + expBatches[b.exp] = append(expBatches[b.exp], ExperimentalBatch{ Thread: b.m, Data: b.data, }) diff --git a/src/internal/trace/internal/testgen/go122/trace.go b/src/internal/trace/internal/testgen/trace.go similarity index 89% rename from src/internal/trace/internal/testgen/go122/trace.go rename to src/internal/trace/internal/testgen/trace.go index 5fd995ae9aeee3..0ae7e9924ed4d5 100644 --- a/src/internal/trace/internal/testgen/go122/trace.go +++ b/src/internal/trace/internal/testgen/trace.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package testkit +package testgen import ( "bytes" @@ -13,14 +13,13 @@ import ( "strings" "internal/trace" - "internal/trace/event" - "internal/trace/event/go122" "internal/trace/raw" + "internal/trace/tracev2" "internal/trace/version" "internal/txtar" ) -func Main(f func(*Trace)) { +func Main(ver version.Version, f func(*Trace)) { // Create an output file. out, err := os.Create(os.Args[1]) if err != nil { @@ -29,7 +28,7 @@ func Main(f func(*Trace)) { defer out.Close() // Create a new trace. - trace := NewTrace() + trace := NewTrace(ver) // Call the generator. f(trace) @@ -51,8 +50,8 @@ func Main(f func(*Trace)) { type Trace struct { // Trace data state. ver version.Version - names map[string]event.Type - specs []event.Spec + names map[string]tracev2.EventType + specs []tracev2.EventSpec events []raw.Event gens []*Generation validTimestamps bool @@ -63,10 +62,9 @@ type Trace struct { } // NewTrace creates a new trace. -func NewTrace() *Trace { - ver := version.Go122 +func NewTrace(ver version.Version) *Trace { return &Trace{ - names: event.Names(ver.Specs()), + names: tracev2.EventNames(ver.Specs()), specs: ver.Specs(), validTimestamps: true, } @@ -87,7 +85,7 @@ func (t *Trace) ExpectSuccess() { // RawEvent emits an event into the trace. name must correspond to one // of the names in Specs() result for the version that was passed to // this trace. -func (t *Trace) RawEvent(typ event.Type, data []byte, args ...uint64) { +func (t *Trace) RawEvent(typ tracev2.EventType, data []byte, args ...uint64) { t.events = append(t.events, t.createEvent(typ, data, args...)) } @@ -147,9 +145,9 @@ func (t *Trace) Generate() []byte { }) } -func (t *Trace) createEvent(ev event.Type, data []byte, args ...uint64) raw.Event { +func (t *Trace) createEvent(ev tracev2.EventType, data []byte, args ...uint64) raw.Event { spec := t.specs[ev] - if ev != go122.EvStack { + if ev != tracev2.EvStack { if arity := len(spec.Args); len(args) != arity { panic(fmt.Sprintf("expected %d args for %s, got %d", arity, spec.Name, len(args))) } @@ -248,22 +246,22 @@ func (g *Generation) writeEventsTo(tw *raw.TextWriter) { // Write frequency. b := g.newStructuralBatch() - b.RawEvent(go122.EvFrequency, nil, 15625000) + b.RawEvent(tracev2.EvFrequency, nil, 15625000) b.writeEventsTo(tw) // Write stacks. b = g.newStructuralBatch() - b.RawEvent(go122.EvStacks, nil) + b.RawEvent(tracev2.EvStacks, nil) for stk, id := range g.stacks { stk := stk.stk[:stk.len] args := []uint64{id} for _, f := range stk { args = append(args, f.PC, g.String(f.Func), g.String(f.File), f.Line) } - b.RawEvent(go122.EvStack, nil, args...) + b.RawEvent(tracev2.EvStack, nil, args...) // Flush the batch if necessary. - if !g.ignoreStackBatchSizeLimit && b.size > go122.MaxBatchSize/2 { + if !g.ignoreStackBatchSizeLimit && b.size > tracev2.MaxBatchSize/2 { b.writeEventsTo(tw) b = g.newStructuralBatch() } @@ -272,12 +270,12 @@ func (g *Generation) writeEventsTo(tw *raw.TextWriter) { // Write strings. b = g.newStructuralBatch() - b.RawEvent(go122.EvStrings, nil) + b.RawEvent(tracev2.EvStrings, nil) for s, id := range g.strings { - b.RawEvent(go122.EvString, []byte(s), id) + b.RawEvent(tracev2.EvString, []byte(s), id) // Flush the batch if necessary. - if !g.ignoreStringBatchSizeLimit && b.size > go122.MaxBatchSize/2 { + if !g.ignoreStringBatchSizeLimit && b.size > tracev2.MaxBatchSize/2 { b.writeEventsTo(tw) b = g.newStructuralBatch() } @@ -341,9 +339,9 @@ func (b *Batch) uintArgFor(arg any, argSpec string) uint64 { case "seq": u = uint64(arg.(Seq)) case "pstatus": - u = uint64(arg.(go122.ProcStatus)) + u = uint64(arg.(tracev2.ProcStatus)) case "gstatus": - u = uint64(arg.(go122.GoStatus)) + u = uint64(arg.(tracev2.GoStatus)) case "g": u = uint64(arg.(trace.GoID)) case "m": @@ -363,7 +361,7 @@ func (b *Batch) uintArgFor(arg any, argSpec string) uint64 { // RawEvent emits an event into a batch. name must correspond to one // of the names in Specs() result for the version that was passed to // this trace. -func (b *Batch) RawEvent(typ event.Type, data []byte, args ...uint64) { +func (b *Batch) RawEvent(typ tracev2.EventType, data []byte, args ...uint64) { ev := b.gen.trace.createEvent(typ, data, args...) // Compute the size of the event and add it to the batch. @@ -385,7 +383,7 @@ func (b *Batch) RawEvent(typ event.Type, data []byte, args ...uint64) { func (b *Batch) writeEventsTo(tw *raw.TextWriter) { tw.WriteEvent(raw.Event{ Version: version.Go122, - Ev: go122.EvEventBatch, + Ev: tracev2.EvEventBatch, Args: []uint64{b.gen.gen, uint64(b.thread), uint64(b.timestamp), b.size}, }) for _, e := range b.events { diff --git a/src/internal/trace/internal/oldtrace/order.go b/src/internal/trace/internal/tracev1/order.go similarity index 99% rename from src/internal/trace/internal/oldtrace/order.go rename to src/internal/trace/internal/tracev1/order.go index b9d63e2f0c22f0..683d7f03b47f2b 100644 --- a/src/internal/trace/internal/oldtrace/order.go +++ b/src/internal/trace/internal/tracev1/order.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package oldtrace +package tracev1 import "errors" diff --git a/src/internal/trace/internal/oldtrace/parser.go b/src/internal/trace/internal/tracev1/parser.go similarity index 89% rename from src/internal/trace/internal/oldtrace/parser.go rename to src/internal/trace/internal/tracev1/parser.go index 0365eeff70b0c8..d47de9088a992b 100644 --- a/src/internal/trace/internal/oldtrace/parser.go +++ b/src/internal/trace/internal/tracev1/parser.go @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package oldtrace implements a parser for Go execution traces from versions +// Package tracev1 implements a parser for Go execution traces from versions // 1.11–1.21. // // The package started as a copy of Go 1.19's internal/trace, but has been // optimized to be faster while using less memory and fewer allocations. It has // been further modified for the specific purpose of converting traces to the // new 1.22+ format. -package oldtrace +package tracev1 import ( "bytes" @@ -17,7 +17,6 @@ import ( "encoding/binary" "errors" "fmt" - "internal/trace/event" "internal/trace/version" "io" "math" @@ -36,12 +35,12 @@ type Event struct { // pointers, the latter so that the garbage collector won't have to scan any // memory of our millions of events. - Ts Timestamp // timestamp in nanoseconds - G uint64 // G on which the event happened - Args [4]uint64 // event-type-specific arguments - StkID uint32 // unique stack ID - P int32 // P on which the event happened (can be a real P or one of TimerP, NetpollP, SyscallP) - Type event.Type // one of Ev* + Ts Timestamp // timestamp in nanoseconds + G uint64 // G on which the event happened + Args [4]uint64 // event-type-specific arguments + StkID uint32 // unique stack ID + P int32 // P on which the event happened (can be a real P or one of TimerP, NetpollP, SyscallP) + Type EventType // one of Ev* } // Frame is a frame in stack traces. @@ -253,7 +252,7 @@ func (p *parser) parse() (Trace, error) { // rawEvent is a helper type used during parsing. type rawEvent struct { - typ event.Type + typ EventType args []uint64 sargs []string @@ -343,6 +342,14 @@ func (l *Events) Pop() (*Event, bool) { return ptr, true } +func (l *Events) Peek() (*Event, bool) { + if l.off == l.n { + return nil, false + } + a, b := l.index(l.off) + return &l.buckets[a][b], true +} + func (l *Events) All() func(yield func(ev *Event) bool) { return func(yield func(ev *Event) bool) { for i := 0; i < l.Len(); i++ { @@ -635,7 +642,7 @@ func (p *parser) readRawEvent(flags uint, ev *rawEvent) error { if !ok { return io.EOF } - typ := event.Type(b << 2 >> 2) + typ := EventType(b << 2 >> 2) // Most events have a timestamp before the actual arguments, so we add 1 and // parse it like it's the first argument. EvString has a special format and // the number of arguments doesn't matter. EvBatch writes '1' as the number @@ -1368,60 +1375,62 @@ func (raw *rawEvent) argNum() int { return narg } +type EventType uint8 + // Event types in the trace. // Verbatim copy from src/runtime/trace.go with the "trace" prefix removed. const ( - EvNone event.Type = 0 // unused - EvBatch event.Type = 1 // start of per-P batch of events [pid, timestamp] - EvFrequency event.Type = 2 // contains tracer timer frequency [frequency (ticks per second)] - EvStack event.Type = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] - EvGomaxprocs event.Type = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] - EvProcStart event.Type = 5 // start of P [timestamp, thread id] - EvProcStop event.Type = 6 // stop of P [timestamp] - EvGCStart event.Type = 7 // GC start [timestamp, seq, stack id] - EvGCDone event.Type = 8 // GC done [timestamp] - EvSTWStart event.Type = 9 // GC mark termination start [timestamp, kind] - EvSTWDone event.Type = 10 // GC mark termination done [timestamp] - EvGCSweepStart event.Type = 11 // GC sweep start [timestamp, stack id] - EvGCSweepDone event.Type = 12 // GC sweep done [timestamp, swept, reclaimed] - EvGoCreate event.Type = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id] - EvGoStart event.Type = 14 // goroutine starts running [timestamp, goroutine id, seq] - EvGoEnd event.Type = 15 // goroutine ends [timestamp] - EvGoStop event.Type = 16 // goroutine stops (like in select{}) [timestamp, stack] - EvGoSched event.Type = 17 // goroutine calls Gosched [timestamp, stack] - EvGoPreempt event.Type = 18 // goroutine is preempted [timestamp, stack] - EvGoSleep event.Type = 19 // goroutine calls Sleep [timestamp, stack] - EvGoBlock event.Type = 20 // goroutine blocks [timestamp, stack] - EvGoUnblock event.Type = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack] - EvGoBlockSend event.Type = 22 // goroutine blocks on chan send [timestamp, stack] - EvGoBlockRecv event.Type = 23 // goroutine blocks on chan recv [timestamp, stack] - EvGoBlockSelect event.Type = 24 // goroutine blocks on select [timestamp, stack] - EvGoBlockSync event.Type = 25 // goroutine blocks on Mutex/RWMutex [timestamp, stack] - EvGoBlockCond event.Type = 26 // goroutine blocks on Cond [timestamp, stack] - EvGoBlockNet event.Type = 27 // goroutine blocks on network [timestamp, stack] - EvGoSysCall event.Type = 28 // syscall enter [timestamp, stack] - EvGoSysExit event.Type = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp] - EvGoSysBlock event.Type = 30 // syscall blocks [timestamp] - EvGoWaiting event.Type = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id] - EvGoInSyscall event.Type = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id] - EvHeapAlloc event.Type = 33 // gcController.heapLive change [timestamp, heap live bytes] - EvHeapGoal event.Type = 34 // gcController.heapGoal change [timestamp, heap goal bytes] - EvTimerGoroutine event.Type = 35 // denotes timer goroutine [timer goroutine id] - EvFutileWakeup event.Type = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] - EvString event.Type = 37 // string dictionary entry [ID, length, string] - EvGoStartLocal event.Type = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id] - EvGoUnblockLocal event.Type = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack] - EvGoSysExitLocal event.Type = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp] - EvGoStartLabel event.Type = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id] - EvGoBlockGC event.Type = 42 // goroutine blocks on GC assist [timestamp, stack] - EvGCMarkAssistStart event.Type = 43 // GC mark assist start [timestamp, stack] - EvGCMarkAssistDone event.Type = 44 // GC mark assist done [timestamp] - EvUserTaskCreate event.Type = 45 // trace.NewTask [timestamp, internal task id, internal parent id, stack, name string] - EvUserTaskEnd event.Type = 46 // end of task [timestamp, internal task id, stack] - EvUserRegion event.Type = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), name string] - EvUserLog event.Type = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] - EvCPUSample event.Type = 49 // CPU profiling sample [timestamp, stack, real timestamp, real P id (-1 when absent), goroutine id] - EvCount event.Type = 50 + EvNone EventType = 0 // unused + EvBatch EventType = 1 // start of per-P batch of events [pid, timestamp] + EvFrequency EventType = 2 // contains tracer timer frequency [frequency (ticks per second)] + EvStack EventType = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] + EvGomaxprocs EventType = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] + EvProcStart EventType = 5 // start of P [timestamp, thread id] + EvProcStop EventType = 6 // stop of P [timestamp] + EvGCStart EventType = 7 // GC start [timestamp, seq, stack id] + EvGCDone EventType = 8 // GC done [timestamp] + EvSTWStart EventType = 9 // GC mark termination start [timestamp, kind] + EvSTWDone EventType = 10 // GC mark termination done [timestamp] + EvGCSweepStart EventType = 11 // GC sweep start [timestamp, stack id] + EvGCSweepDone EventType = 12 // GC sweep done [timestamp, swept, reclaimed] + EvGoCreate EventType = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id] + EvGoStart EventType = 14 // goroutine starts running [timestamp, goroutine id, seq] + EvGoEnd EventType = 15 // goroutine ends [timestamp] + EvGoStop EventType = 16 // goroutine stops (like in select{}) [timestamp, stack] + EvGoSched EventType = 17 // goroutine calls Gosched [timestamp, stack] + EvGoPreempt EventType = 18 // goroutine is preempted [timestamp, stack] + EvGoSleep EventType = 19 // goroutine calls Sleep [timestamp, stack] + EvGoBlock EventType = 20 // goroutine blocks [timestamp, stack] + EvGoUnblock EventType = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack] + EvGoBlockSend EventType = 22 // goroutine blocks on chan send [timestamp, stack] + EvGoBlockRecv EventType = 23 // goroutine blocks on chan recv [timestamp, stack] + EvGoBlockSelect EventType = 24 // goroutine blocks on select [timestamp, stack] + EvGoBlockSync EventType = 25 // goroutine blocks on Mutex/RWMutex [timestamp, stack] + EvGoBlockCond EventType = 26 // goroutine blocks on Cond [timestamp, stack] + EvGoBlockNet EventType = 27 // goroutine blocks on network [timestamp, stack] + EvGoSysCall EventType = 28 // syscall enter [timestamp, stack] + EvGoSysExit EventType = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp] + EvGoSysBlock EventType = 30 // syscall blocks [timestamp] + EvGoWaiting EventType = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id] + EvGoInSyscall EventType = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id] + EvHeapAlloc EventType = 33 // gcController.heapLive change [timestamp, heap live bytes] + EvHeapGoal EventType = 34 // gcController.heapGoal change [timestamp, heap goal bytes] + EvTimerGoroutine EventType = 35 // denotes timer goroutine [timer goroutine id] + EvFutileWakeup EventType = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] + EvString EventType = 37 // string dictionary entry [ID, length, string] + EvGoStartLocal EventType = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id] + EvGoUnblockLocal EventType = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack] + EvGoSysExitLocal EventType = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp] + EvGoStartLabel EventType = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id] + EvGoBlockGC EventType = 42 // goroutine blocks on GC assist [timestamp, stack] + EvGCMarkAssistStart EventType = 43 // GC mark assist start [timestamp, stack] + EvGCMarkAssistDone EventType = 44 // GC mark assist done [timestamp] + EvUserTaskCreate EventType = 45 // trace.NewTask [timestamp, internal task id, internal parent id, stack, name string] + EvUserTaskEnd EventType = 46 // end of task [timestamp, internal task id, stack] + EvUserRegion EventType = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), name string] + EvUserLog EventType = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] + EvCPUSample EventType = 49 // CPU profiling sample [timestamp, stack, real timestamp, real P id (-1 when absent), goroutine id] + EvCount EventType = 50 ) var EventDescriptions = [256]struct { diff --git a/src/internal/trace/internal/oldtrace/parser_test.go b/src/internal/trace/internal/tracev1/parser_test.go similarity index 99% rename from src/internal/trace/internal/oldtrace/parser_test.go rename to src/internal/trace/internal/tracev1/parser_test.go index 6fe31e2e7e1fe1..af6d8db2340eab 100644 --- a/src/internal/trace/internal/oldtrace/parser_test.go +++ b/src/internal/trace/internal/tracev1/parser_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package oldtrace +package tracev1 import ( "bytes" diff --git a/src/internal/trace/internal/oldtrace/testdata/fmt_1_21_pprof_good b/src/internal/trace/internal/tracev1/testdata/fmt_1_21_pprof_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/fmt_1_21_pprof_good rename to src/internal/trace/internal/tracev1/testdata/fmt_1_21_pprof_good diff --git a/src/internal/trace/internal/oldtrace/testdata/http_1_19_good b/src/internal/trace/internal/tracev1/testdata/http_1_19_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/http_1_19_good rename to src/internal/trace/internal/tracev1/testdata/http_1_19_good diff --git a/src/internal/trace/internal/oldtrace/testdata/http_1_21_good b/src/internal/trace/internal/tracev1/testdata/http_1_21_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/http_1_21_good rename to src/internal/trace/internal/tracev1/testdata/http_1_21_good diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_1_11_good b/src/internal/trace/internal/tracev1/testdata/stress_1_11_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/stress_1_11_good rename to src/internal/trace/internal/tracev1/testdata/stress_1_11_good diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_1_19_good b/src/internal/trace/internal/tracev1/testdata/stress_1_19_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/stress_1_19_good rename to src/internal/trace/internal/tracev1/testdata/stress_1_19_good diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_1_21_good b/src/internal/trace/internal/tracev1/testdata/stress_1_21_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/stress_1_21_good rename to src/internal/trace/internal/tracev1/testdata/stress_1_21_good diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_11_good b/src/internal/trace/internal/tracev1/testdata/stress_start_stop_1_11_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_11_good rename to src/internal/trace/internal/tracev1/testdata/stress_start_stop_1_11_good diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_19_good b/src/internal/trace/internal/tracev1/testdata/stress_start_stop_1_19_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_19_good rename to src/internal/trace/internal/tracev1/testdata/stress_start_stop_1_19_good diff --git a/src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_21_good b/src/internal/trace/internal/tracev1/testdata/stress_start_stop_1_21_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/stress_start_stop_1_21_good rename to src/internal/trace/internal/tracev1/testdata/stress_start_stop_1_21_good diff --git a/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_11_good b/src/internal/trace/internal/tracev1/testdata/user_task_region_1_11_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/user_task_region_1_11_good rename to src/internal/trace/internal/tracev1/testdata/user_task_region_1_11_good diff --git a/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_19_good b/src/internal/trace/internal/tracev1/testdata/user_task_region_1_19_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/user_task_region_1_19_good rename to src/internal/trace/internal/tracev1/testdata/user_task_region_1_19_good diff --git a/src/internal/trace/internal/oldtrace/testdata/user_task_region_1_21_good b/src/internal/trace/internal/tracev1/testdata/user_task_region_1_21_good similarity index 100% rename from src/internal/trace/internal/oldtrace/testdata/user_task_region_1_21_good rename to src/internal/trace/internal/tracev1/testdata/user_task_region_1_21_good diff --git a/src/internal/trace/order.go b/src/internal/trace/order.go index d0818a500c03c8..7b6075d5637732 100644 --- a/src/internal/trace/order.go +++ b/src/internal/trace/order.go @@ -6,10 +6,10 @@ package trace import ( "fmt" + "slices" "strings" - "internal/trace/event" - "internal/trace/event/go122" + "internal/trace/tracev2" "internal/trace/version" ) @@ -21,6 +21,7 @@ import ( // add completed events to the ordering. Next is used to pick // off events in the ordering. type ordering struct { + traceVer version.Version gStates map[GoID]*gState pStates map[ProcID]*pState // TODO: The keys are dense, so this can be a slice. mStates map[ThreadID]*mState @@ -87,101 +88,105 @@ func (o *ordering) Advance(ev *baseEvent, evt *evTable, m ThreadID, gen uint64) return ok, err } +func (o *ordering) evName(typ tracev2.EventType) string { + return o.traceVer.EventName(typ) +} + type orderingHandleFunc func(o *ordering, ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) var orderingDispatch = [256]orderingHandleFunc{ // Procs. - go122.EvProcsChange: (*ordering).advanceAnnotation, - go122.EvProcStart: (*ordering).advanceProcStart, - go122.EvProcStop: (*ordering).advanceProcStop, - go122.EvProcSteal: (*ordering).advanceProcSteal, - go122.EvProcStatus: (*ordering).advanceProcStatus, + tracev2.EvProcsChange: (*ordering).advanceAnnotation, + tracev2.EvProcStart: (*ordering).advanceProcStart, + tracev2.EvProcStop: (*ordering).advanceProcStop, + tracev2.EvProcSteal: (*ordering).advanceProcSteal, + tracev2.EvProcStatus: (*ordering).advanceProcStatus, // Goroutines. - go122.EvGoCreate: (*ordering).advanceGoCreate, - go122.EvGoCreateSyscall: (*ordering).advanceGoCreateSyscall, - go122.EvGoStart: (*ordering).advanceGoStart, - go122.EvGoDestroy: (*ordering).advanceGoStopExec, - go122.EvGoDestroySyscall: (*ordering).advanceGoDestroySyscall, - go122.EvGoStop: (*ordering).advanceGoStopExec, - go122.EvGoBlock: (*ordering).advanceGoStopExec, - go122.EvGoUnblock: (*ordering).advanceGoUnblock, - go122.EvGoSyscallBegin: (*ordering).advanceGoSyscallBegin, - go122.EvGoSyscallEnd: (*ordering).advanceGoSyscallEnd, - go122.EvGoSyscallEndBlocked: (*ordering).advanceGoSyscallEndBlocked, - go122.EvGoStatus: (*ordering).advanceGoStatus, + tracev2.EvGoCreate: (*ordering).advanceGoCreate, + tracev2.EvGoCreateSyscall: (*ordering).advanceGoCreateSyscall, + tracev2.EvGoStart: (*ordering).advanceGoStart, + tracev2.EvGoDestroy: (*ordering).advanceGoStopExec, + tracev2.EvGoDestroySyscall: (*ordering).advanceGoDestroySyscall, + tracev2.EvGoStop: (*ordering).advanceGoStopExec, + tracev2.EvGoBlock: (*ordering).advanceGoStopExec, + tracev2.EvGoUnblock: (*ordering).advanceGoUnblock, + tracev2.EvGoSyscallBegin: (*ordering).advanceGoSyscallBegin, + tracev2.EvGoSyscallEnd: (*ordering).advanceGoSyscallEnd, + tracev2.EvGoSyscallEndBlocked: (*ordering).advanceGoSyscallEndBlocked, + tracev2.EvGoStatus: (*ordering).advanceGoStatus, // STW. - go122.EvSTWBegin: (*ordering).advanceGoRangeBegin, - go122.EvSTWEnd: (*ordering).advanceGoRangeEnd, + tracev2.EvSTWBegin: (*ordering).advanceGoRangeBegin, + tracev2.EvSTWEnd: (*ordering).advanceGoRangeEnd, // GC events. - go122.EvGCActive: (*ordering).advanceGCActive, - go122.EvGCBegin: (*ordering).advanceGCBegin, - go122.EvGCEnd: (*ordering).advanceGCEnd, - go122.EvGCSweepActive: (*ordering).advanceGCSweepActive, - go122.EvGCSweepBegin: (*ordering).advanceGCSweepBegin, - go122.EvGCSweepEnd: (*ordering).advanceGCSweepEnd, - go122.EvGCMarkAssistActive: (*ordering).advanceGoRangeActive, - go122.EvGCMarkAssistBegin: (*ordering).advanceGoRangeBegin, - go122.EvGCMarkAssistEnd: (*ordering).advanceGoRangeEnd, - go122.EvHeapAlloc: (*ordering).advanceHeapMetric, - go122.EvHeapGoal: (*ordering).advanceHeapMetric, + tracev2.EvGCActive: (*ordering).advanceGCActive, + tracev2.EvGCBegin: (*ordering).advanceGCBegin, + tracev2.EvGCEnd: (*ordering).advanceGCEnd, + tracev2.EvGCSweepActive: (*ordering).advanceGCSweepActive, + tracev2.EvGCSweepBegin: (*ordering).advanceGCSweepBegin, + tracev2.EvGCSweepEnd: (*ordering).advanceGCSweepEnd, + tracev2.EvGCMarkAssistActive: (*ordering).advanceGoRangeActive, + tracev2.EvGCMarkAssistBegin: (*ordering).advanceGoRangeBegin, + tracev2.EvGCMarkAssistEnd: (*ordering).advanceGoRangeEnd, + tracev2.EvHeapAlloc: (*ordering).advanceHeapMetric, + tracev2.EvHeapGoal: (*ordering).advanceHeapMetric, // Annotations. - go122.EvGoLabel: (*ordering).advanceAnnotation, - go122.EvUserTaskBegin: (*ordering).advanceUserTaskBegin, - go122.EvUserTaskEnd: (*ordering).advanceUserTaskEnd, - go122.EvUserRegionBegin: (*ordering).advanceUserRegionBegin, - go122.EvUserRegionEnd: (*ordering).advanceUserRegionEnd, - go122.EvUserLog: (*ordering).advanceAnnotation, + tracev2.EvGoLabel: (*ordering).advanceAnnotation, + tracev2.EvUserTaskBegin: (*ordering).advanceUserTaskBegin, + tracev2.EvUserTaskEnd: (*ordering).advanceUserTaskEnd, + tracev2.EvUserRegionBegin: (*ordering).advanceUserRegionBegin, + tracev2.EvUserRegionEnd: (*ordering).advanceUserRegionEnd, + tracev2.EvUserLog: (*ordering).advanceAnnotation, // Coroutines. Added in Go 1.23. - go122.EvGoSwitch: (*ordering).advanceGoSwitch, - go122.EvGoSwitchDestroy: (*ordering).advanceGoSwitch, - go122.EvGoCreateBlocked: (*ordering).advanceGoCreate, + tracev2.EvGoSwitch: (*ordering).advanceGoSwitch, + tracev2.EvGoSwitchDestroy: (*ordering).advanceGoSwitch, + tracev2.EvGoCreateBlocked: (*ordering).advanceGoCreate, // GoStatus event with a stack. Added in Go 1.23. - go122.EvGoStatusStack: (*ordering).advanceGoStatus, + tracev2.EvGoStatusStack: (*ordering).advanceGoStatus, // Experimental events. // Experimental heap span events. Added in Go 1.23. - go122.EvSpan: (*ordering).advanceAllocFree, - go122.EvSpanAlloc: (*ordering).advanceAllocFree, - go122.EvSpanFree: (*ordering).advanceAllocFree, + tracev2.EvSpan: (*ordering).advanceAllocFree, + tracev2.EvSpanAlloc: (*ordering).advanceAllocFree, + tracev2.EvSpanFree: (*ordering).advanceAllocFree, // Experimental heap object events. Added in Go 1.23. - go122.EvHeapObject: (*ordering).advanceAllocFree, - go122.EvHeapObjectAlloc: (*ordering).advanceAllocFree, - go122.EvHeapObjectFree: (*ordering).advanceAllocFree, + tracev2.EvHeapObject: (*ordering).advanceAllocFree, + tracev2.EvHeapObjectAlloc: (*ordering).advanceAllocFree, + tracev2.EvHeapObjectFree: (*ordering).advanceAllocFree, // Experimental goroutine stack events. Added in Go 1.23. - go122.EvGoroutineStack: (*ordering).advanceAllocFree, - go122.EvGoroutineStackAlloc: (*ordering).advanceAllocFree, - go122.EvGoroutineStackFree: (*ordering).advanceAllocFree, + tracev2.EvGoroutineStack: (*ordering).advanceAllocFree, + tracev2.EvGoroutineStackAlloc: (*ordering).advanceAllocFree, + tracev2.EvGoroutineStackFree: (*ordering).advanceAllocFree, } func (o *ordering) advanceProcStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { pid := ProcID(ev.args[0]) - status := go122.ProcStatus(ev.args[1]) - if int(status) >= len(go122ProcStatus2ProcState) { + status := tracev2.ProcStatus(ev.args[1]) + if int(status) >= len(tracev2ProcStatus2ProcState) { return curCtx, false, fmt.Errorf("invalid status for proc %d: %d", pid, status) } - oldState := go122ProcStatus2ProcState[status] + oldState := tracev2ProcStatus2ProcState[status] if s, ok := o.pStates[pid]; ok { - if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscall { + if status == tracev2.ProcSyscallAbandoned && s.status == tracev2.ProcSyscall { // ProcSyscallAbandoned is a special case of ProcSyscall. It indicates a // potential loss of information, but if we're already in ProcSyscall, // we haven't lost the relevant information. Promote the status and advance. oldState = ProcRunning - ev.args[1] = uint64(go122.ProcSyscall) - } else if status == go122.ProcSyscallAbandoned && s.status == go122.ProcSyscallAbandoned { + ev.args[1] = uint64(tracev2.ProcSyscall) + } else if status == tracev2.ProcSyscallAbandoned && s.status == tracev2.ProcSyscallAbandoned { // If we're passing through ProcSyscallAbandoned, then there's no promotion // to do. We've lost the M that this P is associated with. However it got there, // it's going to appear as idle in the API, so pass through as idle. oldState = ProcIdle - ev.args[1] = uint64(go122.ProcSyscallAbandoned) + ev.args[1] = uint64(tracev2.ProcSyscallAbandoned) } else if s.status != status { return curCtx, false, fmt.Errorf("inconsistent status for proc %d: old %v vs. new %v", pid, s.status, status) } @@ -198,7 +203,7 @@ func (o *ordering) advanceProcStatus(ev *baseEvent, evt *evTable, m ThreadID, ge // Bind the proc to the new context, if it's running. newCtx := curCtx - if status == go122.ProcRunning || status == go122.ProcSyscall { + if status == tracev2.ProcRunning || status == tracev2.ProcSyscall { newCtx.P = pid } // If we're advancing through ProcSyscallAbandoned *but* oldState is running then we've @@ -207,7 +212,7 @@ func (o *ordering) advanceProcStatus(ev *baseEvent, evt *evTable, m ThreadID, ge // thread it was bound to. Since this status is Running -> Running and Running is binding, // we need to make sure we emit it in the right context: the context to which it is bound. // Find it, and set our current context to it. - if status == go122.ProcSyscallAbandoned && oldState == ProcRunning { + if status == tracev2.ProcSyscallAbandoned && oldState == ProcRunning { // N.B. This is slow but it should be fairly rare. found := false for mid, ms := range o.mStates { @@ -234,7 +239,7 @@ func (o *ordering) advanceProcStart(ev *baseEvent, evt *evTable, m ThreadID, gen // had a status emitted, or because we already have a P and we're in a syscall, // and we haven't observed that it was stolen from us yet. state, ok := o.pStates[pid] - if !ok || state.status != go122.ProcIdle || !seq.succeeds(state.seq) || curCtx.P != NoProc { + if !ok || state.status != tracev2.ProcIdle || !seq.succeeds(state.seq) || curCtx.P != NoProc { // We can't make an inference as to whether this is bad. We could just be seeing // a ProcStart on a different M before the proc's state was emitted, or before we // got to the right point in the trace. @@ -245,11 +250,11 @@ func (o *ordering) advanceProcStart(ev *baseEvent, evt *evTable, m ThreadID, gen // We can advance this P. Check some invariants. // // We might have a goroutine if a goroutine is exiting a syscall. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustNotHave, Goroutine: event.MayHave} + reqs := schedReqs{M: mustHave, P: mustNotHave, G: mayHave} if err := validateCtx(curCtx, reqs); err != nil { return curCtx, false, err } - state.status = go122.ProcRunning + state.status = tracev2.ProcRunning state.seq = seq newCtx := curCtx newCtx.P = pid @@ -269,16 +274,16 @@ func (o *ordering) advanceProcStop(ev *baseEvent, evt *evTable, m ThreadID, gen // ProcStop doesn't need a sequence number. state, ok := o.pStates[curCtx.P] if !ok { - return curCtx, false, fmt.Errorf("event %s for proc (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.P) + return curCtx, false, fmt.Errorf("event %s for proc (%v) that doesn't exist", o.evName(ev.typ), curCtx.P) } - if state.status != go122.ProcRunning && state.status != go122.ProcSyscall { - return curCtx, false, fmt.Errorf("%s event for proc that's not %s or %s", go122.EventString(ev.typ), go122.ProcRunning, go122.ProcSyscall) + if state.status != tracev2.ProcRunning && state.status != tracev2.ProcSyscall { + return curCtx, false, fmt.Errorf("%s event for proc that's not %s or %s", o.evName(ev.typ), tracev2.ProcRunning, tracev2.ProcSyscall) } - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave} + reqs := schedReqs{M: mustHave, P: mustHave, G: mayHave} if err := validateCtx(curCtx, reqs); err != nil { return curCtx, false, err } - state.status = go122.ProcIdle + state.status = tracev2.ProcIdle newCtx := curCtx newCtx.P = NoProc o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -289,14 +294,14 @@ func (o *ordering) advanceProcSteal(ev *baseEvent, evt *evTable, m ThreadID, gen pid := ProcID(ev.args[0]) seq := makeSeq(gen, ev.args[1]) state, ok := o.pStates[pid] - if !ok || (state.status != go122.ProcSyscall && state.status != go122.ProcSyscallAbandoned) || !seq.succeeds(state.seq) { + if !ok || (state.status != tracev2.ProcSyscall && state.status != tracev2.ProcSyscallAbandoned) || !seq.succeeds(state.seq) { // We can't make an inference as to whether this is bad. We could just be seeing // a ProcStart on a different M before the proc's state was emitted, or before we // got to the right point in the trace. return curCtx, false, nil } // We can advance this P. Check some invariants. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave} + reqs := schedReqs{M: mustHave, P: mayHave, G: mayHave} if err := validateCtx(curCtx, reqs); err != nil { return curCtx, false, err } @@ -310,12 +315,12 @@ func (o *ordering) advanceProcSteal(ev *baseEvent, evt *evTable, m ThreadID, gen ev.extra(version.Go122)[0] = uint64(oldStatus) // Update the P's status and sequence number. - state.status = go122.ProcIdle + state.status = tracev2.ProcIdle state.seq = seq // If we've lost information then don't try to do anything with the M. // It may have moved on and we can't be sure. - if oldStatus == go122.ProcSyscallAbandoned { + if oldStatus == tracev2.ProcSyscallAbandoned { o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) return curCtx, true, nil } @@ -359,12 +364,12 @@ func (o *ordering) advanceProcSteal(ev *baseEvent, evt *evTable, m ThreadID, gen func (o *ordering) advanceGoStatus(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { gid := GoID(ev.args[0]) mid := ThreadID(ev.args[1]) - status := go122.GoStatus(ev.args[2]) + status := tracev2.GoStatus(ev.args[2]) - if int(status) >= len(go122GoStatus2GoState) { + if int(status) >= len(tracev2GoStatus2GoState) { return curCtx, false, fmt.Errorf("invalid status for goroutine %d: %d", gid, status) } - oldState := go122GoStatus2GoState[status] + oldState := tracev2GoStatus2GoState[status] if s, ok := o.gStates[gid]; ok { if s.status != status { return curCtx, false, fmt.Errorf("inconsistent status for goroutine %d: old %v vs. new %v", gid, s.status, status) @@ -381,10 +386,10 @@ func (o *ordering) advanceGoStatus(ev *baseEvent, evt *evTable, m ThreadID, gen newCtx := curCtx switch status { - case go122.GoRunning: + case tracev2.GoRunning: // Bind the goroutine to the new context, since it's running. newCtx.G = gid - case go122.GoSyscall: + case tracev2.GoSyscall: if mid == NoThread { return curCtx, false, fmt.Errorf("found goroutine %d in syscall without a thread", gid) } @@ -436,22 +441,22 @@ func (o *ordering) advanceGoStatus(ev *baseEvent, evt *evTable, m ThreadID, gen func (o *ordering) advanceGoCreate(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { // Goroutines must be created on a running P, but may or may not be created // by a running goroutine. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave} + reqs := schedReqs{M: mustHave, P: mustHave, G: mayHave} if err := validateCtx(curCtx, reqs); err != nil { return curCtx, false, err } // If we have a goroutine, it must be running. - if state, ok := o.gStates[curCtx.G]; ok && state.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + if state, ok := o.gStates[curCtx.G]; ok && state.status != tracev2.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", o.evName(ev.typ), GoRunning) } // This goroutine created another. Add a state for it. newgid := GoID(ev.args[0]) if _, ok := o.gStates[newgid]; ok { return curCtx, false, fmt.Errorf("tried to create goroutine (%v) that already exists", newgid) } - status := go122.GoRunnable - if ev.typ == go122.EvGoCreateBlocked { - status = go122.GoWaiting + status := tracev2.GoRunnable + if ev.typ == tracev2.EvGoCreateBlocked { + status = tracev2.GoWaiting } o.gStates[newgid] = &gState{id: newgid, status: status, seq: makeSeq(gen, 0)} o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -462,31 +467,31 @@ func (o *ordering) advanceGoStopExec(ev *baseEvent, evt *evTable, m ThreadID, ge // These are goroutine events that all require an active running // goroutine on some thread. They must *always* be advance-able, // since running goroutines are bound to their M. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } state, ok := o.gStates[curCtx.G] if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", o.evName(ev.typ), curCtx.G) } - if state.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + if state.status != tracev2.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", o.evName(ev.typ), GoRunning) } // Handle each case slightly differently; we just group them together // because they have shared preconditions. newCtx := curCtx switch ev.typ { - case go122.EvGoDestroy: + case tracev2.EvGoDestroy: // This goroutine is exiting itself. delete(o.gStates, curCtx.G) newCtx.G = NoGoroutine - case go122.EvGoStop: + case tracev2.EvGoStop: // Goroutine stopped (yielded). It's runnable but not running on this M. - state.status = go122.GoRunnable + state.status = tracev2.GoRunnable newCtx.G = NoGoroutine - case go122.EvGoBlock: + case tracev2.EvGoBlock: // Goroutine blocked. It's waiting now and not running on this M. - state.status = go122.GoWaiting + state.status = tracev2.GoWaiting newCtx.G = NoGoroutine } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -497,18 +502,18 @@ func (o *ordering) advanceGoStart(ev *baseEvent, evt *evTable, m ThreadID, gen u gid := GoID(ev.args[0]) seq := makeSeq(gen, ev.args[1]) state, ok := o.gStates[gid] - if !ok || state.status != go122.GoRunnable || !seq.succeeds(state.seq) { + if !ok || state.status != tracev2.GoRunnable || !seq.succeeds(state.seq) { // We can't make an inference as to whether this is bad. We could just be seeing // a GoStart on a different M before the goroutine was created, before it had its // state emitted, or before we got to the right point in the trace yet. return curCtx, false, nil } // We can advance this goroutine. Check some invariants. - reqs := event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MustNotHave} + reqs := schedReqs{M: mustHave, P: mustHave, G: mustNotHave} if err := validateCtx(curCtx, reqs); err != nil { return curCtx, false, err } - state.status = go122.GoRunning + state.status = tracev2.GoRunning state.seq = seq newCtx := curCtx newCtx.G = gid @@ -521,13 +526,13 @@ func (o *ordering) advanceGoUnblock(ev *baseEvent, evt *evTable, m ThreadID, gen gid := GoID(ev.args[0]) seq := makeSeq(gen, ev.args[1]) state, ok := o.gStates[gid] - if !ok || state.status != go122.GoWaiting || !seq.succeeds(state.seq) { + if !ok || state.status != tracev2.GoWaiting || !seq.succeeds(state.seq) { // We can't make an inference as to whether this is bad. We could just be seeing // a GoUnblock on a different M before the goroutine was created and blocked itself, // before it had its state emitted, or before we got to the right point in the trace yet. return curCtx, false, nil } - state.status = go122.GoRunnable + state.status = tracev2.GoRunnable state.seq = seq // N.B. No context to validate. Basically anything can unblock // a goroutine (e.g. sysmon). @@ -545,20 +550,20 @@ func (o *ordering) advanceGoSwitch(ev *baseEvent, evt *evTable, m ThreadID, gen // only advance it if the sequence numbers line up. // // The current goroutine on the thread must be actively running. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } curGState, ok := o.gStates[curCtx.G] if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", o.evName(ev.typ), curCtx.G) } - if curGState.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + if curGState.status != tracev2.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", o.evName(ev.typ), GoRunning) } nextg := GoID(ev.args[0]) seq := makeSeq(gen, ev.args[1]) // seq is for nextg, not curCtx.G. nextGState, ok := o.gStates[nextg] - if !ok || nextGState.status != go122.GoWaiting || !seq.succeeds(nextGState.seq) { + if !ok || nextGState.status != tracev2.GoWaiting || !seq.succeeds(nextGState.seq) { // We can't make an inference as to whether this is bad. We could just be seeing // a GoSwitch on a different M before the goroutine was created, before it had its // state emitted, or before we got to the right point in the trace yet. @@ -570,22 +575,22 @@ func (o *ordering) advanceGoSwitch(ev *baseEvent, evt *evTable, m ThreadID, gen // (GoSwitch and GoSwitchDestroy will be interpreted as GoUnblock events // for nextg). switch ev.typ { - case go122.EvGoSwitch: + case tracev2.EvGoSwitch: // Goroutine blocked. It's waiting now and not running on this M. - curGState.status = go122.GoWaiting + curGState.status = tracev2.GoWaiting // Emit a GoBlock event. // TODO(mknyszek): Emit a reason. - o.queue.push(makeEvent(evt, curCtx, go122.EvGoBlock, ev.time, 0 /* no reason */, 0 /* no stack */)) - case go122.EvGoSwitchDestroy: + o.queue.push(makeEvent(evt, curCtx, tracev2.EvGoBlock, ev.time, 0 /* no reason */, 0 /* no stack */)) + case tracev2.EvGoSwitchDestroy: // This goroutine is exiting itself. delete(o.gStates, curCtx.G) // Emit a GoDestroy event. - o.queue.push(makeEvent(evt, curCtx, go122.EvGoDestroy, ev.time)) + o.queue.push(makeEvent(evt, curCtx, tracev2.EvGoDestroy, ev.time)) } // Update the state of the next goroutine. - nextGState.status = go122.GoRunning + nextGState.status = tracev2.GoRunning nextGState.seq = seq newCtx := curCtx newCtx.G = nextg @@ -593,30 +598,30 @@ func (o *ordering) advanceGoSwitch(ev *baseEvent, evt *evTable, m ThreadID, gen // Queue an event for the next goroutine starting to run. startCtx := curCtx startCtx.G = NoGoroutine - o.queue.push(makeEvent(evt, startCtx, go122.EvGoStart, ev.time, uint64(nextg), ev.args[1])) + o.queue.push(makeEvent(evt, startCtx, tracev2.EvGoStart, ev.time, uint64(nextg), ev.args[1])) return newCtx, true, nil } func (o *ordering) advanceGoSyscallBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { // Entering a syscall requires an active running goroutine with a // proc on some thread. It is always advancable. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } state, ok := o.gStates[curCtx.G] if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", o.evName(ev.typ), curCtx.G) } - if state.status != go122.GoRunning { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + if state.status != tracev2.GoRunning { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", o.evName(ev.typ), GoRunning) } // Goroutine entered a syscall. It's still running on this P and M. - state.status = go122.GoSyscall + state.status = tracev2.GoSyscall pState, ok := o.pStates[curCtx.P] if !ok { - return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) + return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, o.evName(ev.typ)) } - pState.status = go122.ProcSyscall + pState.status = tracev2.ProcSyscall // Validate the P sequence number on the event and advance it. // // We have a P sequence number for what is supposed to be a goroutine event @@ -630,7 +635,7 @@ func (o *ordering) advanceGoSyscallBegin(ev *baseEvent, evt *evTable, m ThreadID // to back off and see if any other events will advance. This is a running P. pSeq := makeSeq(gen, ev.args[0]) if !pSeq.succeeds(pState.seq) { - return curCtx, false, fmt.Errorf("failed to advance %s: can't make sequence: %s -> %s", go122.EventString(ev.typ), pState.seq, pSeq) + return curCtx, false, fmt.Errorf("failed to advance %s: can't make sequence: %s -> %s", o.evName(ev.typ), pState.seq, pSeq) } pState.seq = pSeq o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -641,27 +646,27 @@ func (o *ordering) advanceGoSyscallEnd(ev *baseEvent, evt *evTable, m ThreadID, // This event is always advance-able because it happens on the same // thread that EvGoSyscallStart happened, and the goroutine can't leave // that thread until its done. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } state, ok := o.gStates[curCtx.G] if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", o.evName(ev.typ), curCtx.G) } - if state.status != go122.GoSyscall { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + if state.status != tracev2.GoSyscall { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", o.evName(ev.typ), GoRunning) } - state.status = go122.GoRunning + state.status = tracev2.GoRunning // Transfer the P back to running from syscall. pState, ok := o.pStates[curCtx.P] if !ok { - return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) + return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, o.evName(ev.typ)) } - if pState.status != go122.ProcSyscall { - return curCtx, false, fmt.Errorf("expected proc %d in state %v, but got %v instead", curCtx.P, go122.ProcSyscall, pState.status) + if pState.status != tracev2.ProcSyscall { + return curCtx, false, fmt.Errorf("expected proc %d in state %v, but got %v instead", curCtx.P, tracev2.ProcSyscall, pState.status) } - pState.status = go122.ProcRunning + pState.status = tracev2.ProcRunning o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) return curCtx, true, nil } @@ -680,27 +685,27 @@ func (o *ordering) advanceGoSyscallEndBlocked(ev *baseEvent, evt *evTable, m Thr if curCtx.P != NoProc { pState, ok := o.pStates[curCtx.P] if !ok { - return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, go122.EventString(ev.typ)) + return curCtx, false, fmt.Errorf("uninitialized proc %d found during %s", curCtx.P, o.evName(ev.typ)) } - if pState.status == go122.ProcSyscall { + if pState.status == tracev2.ProcSyscall { return curCtx, false, nil } } // As mentioned above, we may have a P here if we ProcStart // before this event. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil { + if err := validateCtx(curCtx, schedReqs{M: mustHave, P: mayHave, G: mustHave}); err != nil { return curCtx, false, err } state, ok := o.gStates[curCtx.G] if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", o.evName(ev.typ), curCtx.G) } - if state.status != go122.GoSyscall { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", go122.EventString(ev.typ), GoRunning) + if state.status != tracev2.GoSyscall { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %s", o.evName(ev.typ), GoRunning) } newCtx := curCtx newCtx.G = NoGoroutine - state.status = go122.GoRunnable + state.status = tracev2.GoRunnable o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) return newCtx, true, nil } @@ -709,7 +714,7 @@ func (o *ordering) advanceGoCreateSyscall(ev *baseEvent, evt *evTable, m ThreadI // This event indicates that a goroutine is effectively // being created out of a cgo callback. Such a goroutine // is 'created' in the syscall state. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustNotHave}); err != nil { + if err := validateCtx(curCtx, schedReqs{M: mustHave, P: mayHave, G: mustNotHave}); err != nil { return curCtx, false, err } // This goroutine is effectively being created. Add a state for it. @@ -717,7 +722,7 @@ func (o *ordering) advanceGoCreateSyscall(ev *baseEvent, evt *evTable, m ThreadI if _, ok := o.gStates[newgid]; ok { return curCtx, false, fmt.Errorf("tried to create goroutine (%v) in syscall that already exists", newgid) } - o.gStates[newgid] = &gState{id: newgid, status: go122.GoSyscall, seq: makeSeq(gen, 0)} + o.gStates[newgid] = &gState{id: newgid, status: tracev2.GoSyscall, seq: makeSeq(gen, 0)} // Goroutine is executing. Bind it to the context. newCtx := curCtx newCtx.G = newgid @@ -742,16 +747,16 @@ func (o *ordering) advanceGoDestroySyscall(ev *baseEvent, evt *evTable, m Thread // Note: we might have a P here. The P might not be released // eagerly by the runtime, and it might get stolen back later // (or never again, if the program is going to exit). - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MustHave}); err != nil { + if err := validateCtx(curCtx, schedReqs{M: mustHave, P: mayHave, G: mustHave}); err != nil { return curCtx, false, err } // Check to make sure the goroutine exists in the right state. state, ok := o.gStates[curCtx.G] if !ok { - return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", go122.EventString(ev.typ), curCtx.G) + return curCtx, false, fmt.Errorf("event %s for goroutine (%v) that doesn't exist", o.evName(ev.typ), curCtx.G) } - if state.status != go122.GoSyscall { - return curCtx, false, fmt.Errorf("%s event for goroutine that's not %v", go122.EventString(ev.typ), GoSyscall) + if state.status != tracev2.GoSyscall { + return curCtx, false, fmt.Errorf("%s event for goroutine that's not %v", o.evName(ev.typ), GoSyscall) } // This goroutine is exiting itself. delete(o.gStates, curCtx.G) @@ -762,18 +767,18 @@ func (o *ordering) advanceGoDestroySyscall(ev *baseEvent, evt *evTable, m Thread if curCtx.P != NoProc { pState, ok := o.pStates[curCtx.P] if !ok { - return curCtx, false, fmt.Errorf("found invalid proc %d during %s", curCtx.P, go122.EventString(ev.typ)) + return curCtx, false, fmt.Errorf("found invalid proc %d during %s", curCtx.P, o.evName(ev.typ)) } - if pState.status != go122.ProcSyscall { - return curCtx, false, fmt.Errorf("proc %d in unexpected state %s during %s", curCtx.P, pState.status, go122.EventString(ev.typ)) + if pState.status != tracev2.ProcSyscall { + return curCtx, false, fmt.Errorf("proc %d in unexpected state %s during %s", curCtx.P, pState.status, o.evName(ev.typ)) } // See the go122-create-syscall-reuse-thread-id test case for more details. - pState.status = go122.ProcSyscallAbandoned + pState.status = tracev2.ProcSyscallAbandoned newCtx.P = NoProc // Queue an extra self-ProcSteal event. - extra := makeEvent(evt, curCtx, go122.EvProcSteal, ev.time, uint64(curCtx.P)) - extra.base.extra(version.Go122)[0] = uint64(go122.ProcSyscall) + extra := makeEvent(evt, curCtx, tracev2.EvProcSteal, ev.time, uint64(curCtx.P)) + extra.base.extra(version.Go122)[0] = uint64(tracev2.ProcSyscall) o.queue.push(extra) } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -811,7 +816,7 @@ func (o *ordering) advanceUserTaskBegin(ev *baseEvent, evt *evTable, m ThreadID, return curCtx, false, fmt.Errorf("invalid string ID %v for %v event", nameID, ev.typ) } o.activeTasks[id] = taskState{name: name, parentID: parentID} - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -832,7 +837,7 @@ func (o *ordering) advanceUserTaskEnd(ev *baseEvent, evt *evTable, m ThreadID, g ev.extra(version.Go122)[0] = uint64(NoTask) ev.extra(version.Go122)[1] = uint64(evt.addExtraString("")) } - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -840,7 +845,7 @@ func (o *ordering) advanceUserTaskEnd(ev *baseEvent, evt *evTable, m ThreadID, g } func (o *ordering) advanceUserRegionBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } tid := TaskID(ev.args[0]) @@ -861,7 +866,7 @@ func (o *ordering) advanceUserRegionBegin(ev *baseEvent, evt *evTable, m ThreadI } func (o *ordering) advanceUserRegionEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } tid := TaskID(ev.args[0]) @@ -907,7 +912,7 @@ func (o *ordering) advanceGCActive(ev *baseEvent, evt *evTable, m ThreadID, gen return curCtx, false, fmt.Errorf("encountered GCActive while GC was not in progress") } o.gcSeq = seq - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -931,7 +936,7 @@ func (o *ordering) advanceGCBegin(ev *baseEvent, evt *evTable, m ThreadID, gen u } o.gcSeq = seq o.gcState = gcRunning - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -952,7 +957,7 @@ func (o *ordering) advanceGCEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uin } o.gcSeq = seq o.gcState = gcNotRunning - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -961,7 +966,7 @@ func (o *ordering) advanceGCEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uin func (o *ordering) advanceAnnotation(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { // Handle simple instantaneous events that require a G. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -970,7 +975,7 @@ func (o *ordering) advanceAnnotation(ev *baseEvent, evt *evTable, m ThreadID, ge func (o *ordering) advanceHeapMetric(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { // Handle allocation metrics, which don't require a G. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { + if err := validateCtx(curCtx, schedReqs{M: mustHave, P: mustHave, G: mayHave}); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -979,7 +984,7 @@ func (o *ordering) advanceHeapMetric(ev *baseEvent, evt *evTable, m ThreadID, ge func (o *ordering) advanceGCSweepBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { // Handle sweep, which is bound to a P and doesn't require a G. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { + if err := validateCtx(curCtx, schedReqs{M: mustHave, P: mustHave, G: mayHave}); err != nil { return curCtx, false, err } if err := o.pStates[curCtx.P].beginRange(makeRangeType(ev.typ, 0)); err != nil { @@ -1007,7 +1012,7 @@ func (o *ordering) advanceGCSweepActive(ev *baseEvent, evt *evTable, m ThreadID, } func (o *ordering) advanceGCSweepEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MustHave, Goroutine: event.MayHave}); err != nil { + if err := validateCtx(curCtx, schedReqs{M: mustHave, P: mustHave, G: mayHave}); err != nil { return curCtx, false, err } _, err := o.pStates[curCtx.P].endRange(ev.typ) @@ -1020,11 +1025,11 @@ func (o *ordering) advanceGCSweepEnd(ev *baseEvent, evt *evTable, m ThreadID, ge func (o *ordering) advanceGoRangeBegin(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { // Handle special goroutine-bound event ranges. - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } desc := stringID(0) - if ev.typ == go122.EvSTWBegin { + if ev.typ == tracev2.EvSTWBegin { desc = stringID(ev.args[0]) } gState, ok := o.gStates[curCtx.G] @@ -1045,7 +1050,7 @@ func (o *ordering) advanceGoRangeActive(ev *baseEvent, evt *evTable, m ThreadID, // current scheduler context. gState, ok := o.gStates[gid] if !ok { - return curCtx, false, fmt.Errorf("uninitialized goroutine %d found during %s", gid, go122.EventString(ev.typ)) + return curCtx, false, fmt.Errorf("uninitialized goroutine %d found during %s", gid, o.evName(ev.typ)) } if err := gState.activeRange(makeRangeType(ev.typ, 0), gen == o.initialGen); err != nil { return curCtx, false, err @@ -1055,7 +1060,7 @@ func (o *ordering) advanceGoRangeActive(ev *baseEvent, evt *evTable, m ThreadID, } func (o *ordering) advanceGoRangeEnd(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { - if err := validateCtx(curCtx, event.UserGoReqs); err != nil { + if err := validateCtx(curCtx, userGoReqs); err != nil { return curCtx, false, err } gState, ok := o.gStates[curCtx.G] @@ -1066,7 +1071,7 @@ func (o *ordering) advanceGoRangeEnd(ev *baseEvent, evt *evTable, m ThreadID, ge if err != nil { return curCtx, false, err } - if ev.typ == go122.EvSTWEnd { + if ev.typ == tracev2.EvSTWEnd { // Smuggle the kind into the event. // Don't use ev.extra here so we have symmetry with STWBegin. ev.args[0] = uint64(desc) @@ -1077,7 +1082,7 @@ func (o *ordering) advanceGoRangeEnd(ev *baseEvent, evt *evTable, m ThreadID, ge func (o *ordering) advanceAllocFree(ev *baseEvent, evt *evTable, m ThreadID, gen uint64, curCtx schedCtx) (schedCtx, bool, error) { // Handle simple instantaneous events that may or may not have a P. - if err := validateCtx(curCtx, event.SchedReqs{Thread: event.MustHave, Proc: event.MayHave, Goroutine: event.MayHave}); err != nil { + if err := validateCtx(curCtx, schedReqs{M: mustHave, P: mayHave, G: mayHave}); err != nil { return curCtx, false, err } o.queue.push(Event{table: evt, ctx: curCtx, base: *ev}) @@ -1098,25 +1103,25 @@ type schedCtx struct { // validateCtx ensures that ctx conforms to some reqs, returning an error if // it doesn't. -func validateCtx(ctx schedCtx, reqs event.SchedReqs) error { +func validateCtx(ctx schedCtx, reqs schedReqs) error { // Check thread requirements. - if reqs.Thread == event.MustHave && ctx.M == NoThread { + if reqs.M == mustHave && ctx.M == NoThread { return fmt.Errorf("expected a thread but didn't have one") - } else if reqs.Thread == event.MustNotHave && ctx.M != NoThread { + } else if reqs.M == mustNotHave && ctx.M != NoThread { return fmt.Errorf("expected no thread but had one") } // Check proc requirements. - if reqs.Proc == event.MustHave && ctx.P == NoProc { + if reqs.P == mustHave && ctx.P == NoProc { return fmt.Errorf("expected a proc but didn't have one") - } else if reqs.Proc == event.MustNotHave && ctx.P != NoProc { + } else if reqs.P == mustNotHave && ctx.P != NoProc { return fmt.Errorf("expected no proc but had one") } // Check goroutine requirements. - if reqs.Goroutine == event.MustHave && ctx.G == NoGoroutine { + if reqs.G == mustHave && ctx.G == NoGoroutine { return fmt.Errorf("expected a goroutine but didn't have one") - } else if reqs.Goroutine == event.MustNotHave && ctx.G != NoGoroutine { + } else if reqs.G == mustNotHave && ctx.G != NoGoroutine { return fmt.Errorf("expected no goroutine but had one") } return nil @@ -1161,13 +1166,13 @@ type userRegion struct { // they may have an optional subtype that describes the range // in more detail. type rangeType struct { - typ event.Type // "Begin" event. - desc stringID // Optional subtype. + typ tracev2.EventType // "Begin" event. + desc stringID // Optional subtype. } // makeRangeType constructs a new rangeType. -func makeRangeType(typ event.Type, desc stringID) rangeType { - if styp := go122.Specs()[typ].StartEv; styp != go122.EvNone { +func makeRangeType(typ tracev2.EventType, desc stringID) rangeType { + if styp := tracev2.Specs()[typ].StartEv; styp != tracev2.EvNone { typ = styp } return rangeType{typ, desc} @@ -1176,7 +1181,7 @@ func makeRangeType(typ event.Type, desc stringID) rangeType { // gState is the state of a goroutine at a point in the trace. type gState struct { id GoID - status go122.GoStatus + status tracev2.GoStatus seq seqCounter // regions are the active user regions for this goroutine. @@ -1208,7 +1213,7 @@ func (s *gState) endRegion(r userRegion) error { // pState is the state of a proc at a point in the trace. type pState struct { id ProcID - status go122.ProcStatus + status tracev2.ProcStatus seq seqCounter // rangeState is the state of special time ranges bound to this proc. @@ -1232,7 +1237,7 @@ type rangeState struct { // Returns an error if the range is already in progress. func (s *rangeState) beginRange(typ rangeType) error { if s.hasRange(typ) { - return fmt.Errorf("discovered event already in-flight for when starting event %v", go122.Specs()[typ.typ].Name) + return fmt.Errorf("discovered event already in-flight for when starting event %v", tracev2.Specs()[typ.typ].Name) } s.inFlight = append(s.inFlight, typ) return nil @@ -1247,26 +1252,21 @@ func (s *rangeState) activeRange(typ rangeType, isInitialGen bool) error { } s.inFlight = append(s.inFlight, typ) } else if !s.hasRange(typ) { - return fmt.Errorf("resource is missing active range: %v %v", go122.Specs()[typ.typ].Name, s.inFlight) + return fmt.Errorf("resource is missing active range: %v %v", tracev2.Specs()[typ.typ].Name, s.inFlight) } return nil } // hasRange returns true if a special time range on the goroutine as in progress. func (s *rangeState) hasRange(typ rangeType) bool { - for _, ftyp := range s.inFlight { - if ftyp == typ { - return true - } - } - return false + return slices.Contains(s.inFlight, typ) } // endRange ends a special range in time on the goroutine. // // This must line up with the start event type of the range the goroutine is currently in. -func (s *rangeState) endRange(typ event.Type) (stringID, error) { - st := go122.Specs()[typ].StartEv +func (s *rangeState) endRange(typ tracev2.EventType) (stringID, error) { + st := tracev2.Specs()[typ].StartEv idx := -1 for i, r := range s.inFlight { if r.typ == st { @@ -1275,7 +1275,7 @@ func (s *rangeState) endRange(typ event.Type) (stringID, error) { } } if idx < 0 { - return 0, fmt.Errorf("tried to end event %v, but not in-flight", go122.Specs()[st].Name) + return 0, fmt.Errorf("tried to end event %v, but not in-flight", tracev2.Specs()[st].Name) } // Swap remove. desc := s.inFlight[idx].desc @@ -1385,7 +1385,7 @@ func (q *queue[T]) pop() (T, bool) { // It's just a convenience function; it's always OK to construct // an Event manually if this isn't quite the right way to express // the contents of the event. -func makeEvent(table *evTable, ctx schedCtx, typ event.Type, time Time, args ...uint64) Event { +func makeEvent(table *evTable, ctx schedCtx, typ tracev2.EventType, time Time, args ...uint64) Event { ev := Event{ table: table, ctx: ctx, @@ -1397,3 +1397,24 @@ func makeEvent(table *evTable, ctx schedCtx, typ event.Type, time Time, args ... copy(ev.base.args[:], args) return ev } + +// schedReqs is a set of constraints on what the scheduling +// context must look like. +type schedReqs struct { + M constraint + P constraint + G constraint +} + +// constraint represents a various presence requirements. +type constraint uint8 + +const ( + mustNotHave constraint = iota + mayHave + mustHave +) + +// userGoReqs is a common requirement among events that are running +// or are close to running user code. +var userGoReqs = schedReqs{M: mustHave, P: mustHave, G: mustHave} diff --git a/src/internal/trace/parser.go b/src/internal/trace/parser.go deleted file mode 100644 index d6fff84d55b63e..00000000000000 --- a/src/internal/trace/parser.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2014 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. - -package trace - -// Frame is a frame in stack traces. -type Frame struct { - PC uint64 - Fn string - File string - Line int -} - -const ( - // Special P identifiers: - FakeP = 1000000 + iota - TimerP // depicts timer unblocks - NetpollP // depicts network unblocks - SyscallP // depicts returns from syscalls - GCP // depicts GC state - ProfileP // depicts recording of CPU profile samples -) - -// Event types in the trace. -// Verbatim copy from src/runtime/trace.go with the "trace" prefix removed. -const ( - EvNone = 0 // unused - EvBatch = 1 // start of per-P batch of events [pid, timestamp] - EvFrequency = 2 // contains tracer timer frequency [frequency (ticks per second)] - EvStack = 3 // stack [stack id, number of PCs, array of {PC, func string ID, file string ID, line}] - EvGomaxprocs = 4 // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack id] - EvProcStart = 5 // start of P [timestamp, thread id] - EvProcStop = 6 // stop of P [timestamp] - EvGCStart = 7 // GC start [timestamp, seq, stack id] - EvGCDone = 8 // GC done [timestamp] - EvSTWStart = 9 // GC mark termination start [timestamp, kind] - EvSTWDone = 10 // GC mark termination done [timestamp] - EvGCSweepStart = 11 // GC sweep start [timestamp, stack id] - EvGCSweepDone = 12 // GC sweep done [timestamp, swept, reclaimed] - EvGoCreate = 13 // goroutine creation [timestamp, new goroutine id, new stack id, stack id] - EvGoStart = 14 // goroutine starts running [timestamp, goroutine id, seq] - EvGoEnd = 15 // goroutine ends [timestamp] - EvGoStop = 16 // goroutine stops (like in select{}) [timestamp, stack] - EvGoSched = 17 // goroutine calls Gosched [timestamp, stack] - EvGoPreempt = 18 // goroutine is preempted [timestamp, stack] - EvGoSleep = 19 // goroutine calls Sleep [timestamp, stack] - EvGoBlock = 20 // goroutine blocks [timestamp, stack] - EvGoUnblock = 21 // goroutine is unblocked [timestamp, goroutine id, seq, stack] - EvGoBlockSend = 22 // goroutine blocks on chan send [timestamp, stack] - EvGoBlockRecv = 23 // goroutine blocks on chan recv [timestamp, stack] - EvGoBlockSelect = 24 // goroutine blocks on select [timestamp, stack] - EvGoBlockSync = 25 // goroutine blocks on Mutex/RWMutex [timestamp, stack] - EvGoBlockCond = 26 // goroutine blocks on Cond [timestamp, stack] - EvGoBlockNet = 27 // goroutine blocks on network [timestamp, stack] - EvGoSysCall = 28 // syscall enter [timestamp, stack] - EvGoSysExit = 29 // syscall exit [timestamp, goroutine id, seq, real timestamp] - EvGoSysBlock = 30 // syscall blocks [timestamp] - EvGoWaiting = 31 // denotes that goroutine is blocked when tracing starts [timestamp, goroutine id] - EvGoInSyscall = 32 // denotes that goroutine is in syscall when tracing starts [timestamp, goroutine id] - EvHeapAlloc = 33 // gcController.heapLive change [timestamp, heap live bytes] - EvHeapGoal = 34 // gcController.heapGoal change [timestamp, heap goal bytes] - EvTimerGoroutine = 35 // denotes timer goroutine [timer goroutine id] - EvFutileWakeup = 36 // denotes that the previous wakeup of this goroutine was futile [timestamp] - EvString = 37 // string dictionary entry [ID, length, string] - EvGoStartLocal = 38 // goroutine starts running on the same P as the last event [timestamp, goroutine id] - EvGoUnblockLocal = 39 // goroutine is unblocked on the same P as the last event [timestamp, goroutine id, stack] - EvGoSysExitLocal = 40 // syscall exit on the same P as the last event [timestamp, goroutine id, real timestamp] - EvGoStartLabel = 41 // goroutine starts running with label [timestamp, goroutine id, seq, label string id] - EvGoBlockGC = 42 // goroutine blocks on GC assist [timestamp, stack] - EvGCMarkAssistStart = 43 // GC mark assist start [timestamp, stack] - EvGCMarkAssistDone = 44 // GC mark assist done [timestamp] - EvUserTaskCreate = 45 // trace.NewTask [timestamp, internal task id, internal parent id, name string, stack] - EvUserTaskEnd = 46 // end of task [timestamp, internal task id, stack] - EvUserRegion = 47 // trace.WithRegion [timestamp, internal task id, mode(0:start, 1:end), name string, stack] - EvUserLog = 48 // trace.Log [timestamp, internal id, key string id, stack, value string] - EvCPUSample = 49 // CPU profiling sample [timestamp, real timestamp, real P id (-1 when absent), goroutine id, stack] - EvCount = 50 -) diff --git a/src/internal/trace/raw/event.go b/src/internal/trace/raw/event.go index e163a2c6ef820b..9042d3f2151eb4 100644 --- a/src/internal/trace/raw/event.go +++ b/src/internal/trace/raw/event.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "internal/trace/event" + "internal/trace/tracev2" "internal/trace/version" ) @@ -20,7 +20,7 @@ import ( // trace format's framing. (But not interpreted.) type Event struct { Version version.Version - Ev event.Type + Ev tracev2.EventType Args []uint64 Data []byte } diff --git a/src/internal/trace/raw/reader.go b/src/internal/trace/raw/reader.go index 37f36a1a24b712..af5dfac0e77a72 100644 --- a/src/internal/trace/raw/reader.go +++ b/src/internal/trace/raw/reader.go @@ -10,7 +10,7 @@ import ( "fmt" "io" - "internal/trace/event" + "internal/trace/tracev2" "internal/trace/version" ) @@ -19,7 +19,7 @@ import ( type Reader struct { r *bufio.Reader v version.Version - specs []event.Spec + specs []tracev2.EventSpec } // NewReader creates a new reader for the trace wire format. @@ -49,7 +49,7 @@ func (r *Reader) ReadEvent() (Event, error) { if int(evb) >= len(r.specs) || evb == 0 { return Event{}, fmt.Errorf("invalid event type: %d", evb) } - ev := event.Type(evb) + ev := tracev2.EventType(evb) spec := r.specs[ev] args, err := r.readArgs(len(spec.Args)) if err != nil { diff --git a/src/internal/trace/raw/textreader.go b/src/internal/trace/raw/textreader.go index 37bfcfefdf62f7..adb111550d1ee7 100644 --- a/src/internal/trace/raw/textreader.go +++ b/src/internal/trace/raw/textreader.go @@ -12,7 +12,7 @@ import ( "strings" "unicode" - "internal/trace/event" + "internal/trace/tracev2" "internal/trace/version" ) @@ -20,8 +20,8 @@ import ( // into an event stream. type TextReader struct { v version.Version - specs []event.Spec - names map[string]event.Type + specs []tracev2.EventSpec + names map[string]tracev2.EventType s *bufio.Scanner } @@ -50,7 +50,7 @@ func NewTextReader(r io.Reader) (*TextReader, error) { } tr.v = v tr.specs = v.Specs() - tr.names = event.Names(tr.specs) + tr.names = tracev2.EventNames(tr.specs) for _, r := range line { if !unicode.IsSpace(r) { return nil, fmt.Errorf("encountered unexpected non-space at the end of the header: %q", line) diff --git a/src/internal/trace/raw/writer.go b/src/internal/trace/raw/writer.go index 9b87995aa77738..6b7042cf2af101 100644 --- a/src/internal/trace/raw/writer.go +++ b/src/internal/trace/raw/writer.go @@ -9,7 +9,7 @@ import ( "fmt" "io" - "internal/trace/event" + "internal/trace/tracev2" "internal/trace/version" ) @@ -23,7 +23,7 @@ type Writer struct { w io.Writer buf []byte v version.Version - specs []event.Spec + specs []tracev2.EventSpec } // NewWriter creates a new byte format writer. diff --git a/src/internal/trace/reader.go b/src/internal/trace/reader.go index 81157292fb3f08..7212a424d79c5d 100644 --- a/src/internal/trace/reader.go +++ b/src/internal/trace/reader.go @@ -11,24 +11,31 @@ import ( "slices" "strings" - "internal/trace/event/go122" - "internal/trace/internal/oldtrace" + "internal/trace/internal/tracev1" + "internal/trace/tracev2" "internal/trace/version" ) // Reader reads a byte stream, validates it, and produces trace events. +// +// Provided the trace is non-empty the Reader always produces a Sync +// event as the first event, and a Sync event as the last event. +// (There may also be any number of Sync events in the middle, too.) type Reader struct { - r *bufio.Reader - lastTs Time - gen *generation - spill *spilledBatch - spillErr error // error from reading spill - frontier []*batchCursor - cpuSamples []cpuSample - order ordering - emittedSync bool - - go121Events *oldTraceConverter + version version.Version + r *bufio.Reader + lastTs Time + gen *generation + spill *spilledBatch + spillErr error // error from reading spill + spillErrSync bool // whether we emitted a Sync before reporting spillErr + frontier []*batchCursor + cpuSamples []cpuSample + order ordering + syncs int + done bool + + v1Events *traceV1Converter } // NewReader creates a new trace reader. @@ -40,24 +47,24 @@ func NewReader(r io.Reader) (*Reader, error) { } switch v { case version.Go111, version.Go119, version.Go121: - tr, err := oldtrace.Parse(br, v) + tr, err := tracev1.Parse(br, v) if err != nil { return nil, err } return &Reader{ - go121Events: convertOldFormat(tr), + v1Events: convertV1Trace(tr), }, nil case version.Go122, version.Go123: return &Reader{ - r: br, + version: v, + r: br, order: ordering{ + traceVer: v, mStates: make(map[ThreadID]*mState), pStates: make(map[ProcID]*pState), gStates: make(map[GoID]*gState), activeTasks: make(map[TaskID]taskState), }, - // Don't emit a sync event when we first go to emit events. - emittedSync: true, }, nil default: return nil, fmt.Errorf("unknown or unsupported version go 1.%d", v) @@ -66,19 +73,36 @@ func NewReader(r io.Reader) (*Reader, error) { // ReadEvent reads a single event from the stream. // -// If the stream has been exhausted, it returns an invalid -// event and io.EOF. +// If the stream has been exhausted, it returns an invalid event and io.EOF. func (r *Reader) ReadEvent() (e Event, err error) { - if r.go121Events != nil { - ev, err := r.go121Events.next() - if err != nil { - // XXX do we have to emit an EventSync when the trace is done? + // Return only io.EOF if we're done. + if r.done { + return Event{}, io.EOF + } + + // Handle v1 execution traces. + if r.v1Events != nil { + if r.syncs == 0 { + // Always emit a sync event first, if we have any events at all. + ev, ok := r.v1Events.events.Peek() + if ok { + r.syncs++ + return syncEvent(r.v1Events.evt, Time(ev.Ts-1), r.syncs), nil + } + } + ev, err := r.v1Events.next() + if err == io.EOF { + // Always emit a sync event at the end. + r.done = true + r.syncs++ + return syncEvent(nil, r.v1Events.lastTs+1, r.syncs), nil + } else if err != nil { return Event{}, err } return ev, nil } - // Go 1.22+ trace parsing algorithm. + // Trace v2 parsing algorithm. // // (1) Read in all the batches for the next generation from the stream. // (a) Use the size field in the header to quickly find all batches. @@ -115,28 +139,31 @@ func (r *Reader) ReadEvent() (e Event, err error) { // Check if we need to refresh the generation. if len(r.frontier) == 0 && len(r.cpuSamples) == 0 { - if !r.emittedSync { - r.emittedSync = true - return syncEvent(r.gen.evTable, r.lastTs), nil - } if r.spillErr != nil { - return Event{}, r.spillErr + if r.spillErrSync { + return Event{}, r.spillErr + } + r.spillErrSync = true + r.syncs++ + return syncEvent(nil, r.lastTs, r.syncs), nil } if r.gen != nil && r.spill == nil { // If we have a generation from the last read, // and there's nothing left in the frontier, and // there's no spilled batch, indicating that there's // no further generation, it means we're done. - // Return io.EOF. - return Event{}, io.EOF + // Emit the final sync event. + r.done = true + r.syncs++ + return syncEvent(nil, r.lastTs, r.syncs), nil } // Read the next generation. - var err error - r.gen, r.spill, err = readGeneration(r.r, r.spill) + r.gen, r.spill, r.spillErr = readGeneration(r.r, r.spill) if r.gen == nil { - return Event{}, err + r.spillErrSync = true + r.syncs++ + return syncEvent(nil, r.lastTs, r.syncs), nil } - r.spillErr = err // Reset CPU samples cursor. r.cpuSamples = r.gen.cpuSamples @@ -155,9 +182,12 @@ func (r *Reader) ReadEvent() (e Event, err error) { } r.frontier = heapInsert(r.frontier, bc) } - - // Reset emittedSync. - r.emittedSync = false + r.syncs++ + if r.lastTs == 0 { + r.lastTs = r.gen.freq.mul(r.gen.minTs) + } + // Always emit a sync event at the beginning of the generation. + return syncEvent(r.gen.evTable, r.lastTs, r.syncs), nil } tryAdvance := func(i int) (bool, error) { bc := r.frontier[i] @@ -227,7 +257,7 @@ func (r *Reader) ReadEvent() (e Event, err error) { func dumpFrontier(frontier []*batchCursor) string { var sb strings.Builder for _, bc := range frontier { - spec := go122.Specs()[bc.ev.typ] + spec := tracev2.Specs()[bc.ev.typ] fmt.Fprintf(&sb, "M %d [%s time=%d", bc.m, spec.Name, bc.ev.time) for i, arg := range spec.Args[1:] { fmt.Fprintf(&sb, " %s=%d", arg, bc.ev.args[i]) diff --git a/src/internal/trace/summary_test.go b/src/internal/trace/summary_test.go index 73865652ebac44..396bd5f0969f06 100644 --- a/src/internal/trace/summary_test.go +++ b/src/internal/trace/summary_test.go @@ -416,10 +416,10 @@ func TestRelatedGoroutinesV2Trace(t *testing.T) { targetg := trace.GoID(86) got := trace.RelatedGoroutinesV2(events, targetg) want := map[trace.GoID]struct{}{ - trace.GoID(86): struct{}{}, // N.B. Result includes target. - trace.GoID(71): struct{}{}, - trace.GoID(25): struct{}{}, - trace.GoID(122): struct{}{}, + trace.GoID(86): {}, // N.B. Result includes target. + trace.GoID(71): {}, + trace.GoID(25): {}, + trace.GoID(122): {}, } for goid := range got { if _, ok := want[goid]; ok { diff --git a/src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go b/src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go index 9f27ae046ff49c..9b98723c4de8b1 100644 --- a/src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go +++ b/src/internal/trace/testdata/generators/go122-confuse-seq-across-generations.go @@ -23,12 +23,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -36,13 +37,13 @@ func gen(t *testgen.Trace) { // A running goroutine blocks. b10 := g1.Batch(trace.ThreadID(0), 0) - b10.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b10.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b10.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b10.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b10.Event("GoStop", "whatever", testgen.NoStack) // The running goroutine gets unblocked. b11 := g1.Batch(trace.ThreadID(1), 0) - b11.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) + b11.Event("ProcStatus", trace.ProcID(1), tracev2.ProcRunning) b11.Event("GoStart", trace.GoID(1), testgen.Seq(1)) b11.Event("GoStop", "whatever", testgen.NoStack) @@ -50,13 +51,13 @@ func gen(t *testgen.Trace) { // Start running the goroutine, but later. b21 := g2.Batch(trace.ThreadID(1), 3) - b21.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) + b21.Event("ProcStatus", trace.ProcID(1), tracev2.ProcRunning) b21.Event("GoStart", trace.GoID(1), testgen.Seq(2)) // The goroutine starts running, then stops, then starts again. b20 := g2.Batch(trace.ThreadID(0), 5) - b20.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b20.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunnable) + b20.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b20.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunnable) b20.Event("GoStart", trace.GoID(1), testgen.Seq(1)) b20.Event("GoStop", "whatever", testgen.NoStack) } diff --git a/src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go b/src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go index e5081598325c06..dc5c4a52572caf 100644 --- a/src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go +++ b/src/internal/trace/testdata/generators/go122-create-syscall-reuse-thread-id.go @@ -25,12 +25,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -41,9 +42,9 @@ func gen(t *testgen.Trace) { b0 := g.Batch(trace.ThreadID(0), 0) b0.Event("GoCreateSyscall", trace.GoID(4)) b0.Event("GoSyscallEndBlocked") - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcIdle) b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) - b0.Event("GoStatus", trace.GoID(4), trace.NoThread, go122.GoRunnable) + b0.Event("GoStatus", trace.GoID(4), trace.NoThread, tracev2.GoRunnable) b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) b0.Event("GoDestroySyscall") @@ -55,7 +56,7 @@ func gen(t *testgen.Trace) { // have a self-steal here potentially that doesn't make // sense. b1 := g.Batch(trace.ThreadID(0), 0) - b1.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b1.Event("ProcStatus", trace.ProcID(1), tracev2.ProcIdle) b1.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(3), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-create-syscall-with-p.go b/src/internal/trace/testdata/generators/go122-create-syscall-with-p.go index 681464ce19d2f5..90729d7c528881 100644 --- a/src/internal/trace/testdata/generators/go122-create-syscall-with-p.go +++ b/src/internal/trace/testdata/generators/go122-create-syscall-with-p.go @@ -16,12 +16,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -39,7 +40,7 @@ func gen(t *testgen.Trace) { // possible on other platforms, however. b0 := g.Batch(trace.ThreadID(0), 0) b0.Event("GoCreateSyscall", trace.GoID(4)) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcIdle) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcIdle) b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(1)) b0.Event("GoSyscallEndBlocked") b0.Event("GoStart", trace.GoID(4), testgen.Seq(1)) diff --git a/src/internal/trace/testdata/generators/go122-fail-first-gen-first.go b/src/internal/trace/testdata/generators/go122-fail-first-gen-first.go index f0e02be15560d2..c8ead6772c0dcb 100644 --- a/src/internal/trace/testdata/generators/go122-fail-first-gen-first.go +++ b/src/internal/trace/testdata/generators/go122-fail-first-gen-first.go @@ -19,25 +19,26 @@ package main import ( - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { // A running goroutine emits a task begin. - t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) - t.RawEvent(go122.EvFrequency, nil, 15625000) + t.RawEvent(tracev2.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) + t.RawEvent(tracev2.EvFrequency, nil, 15625000) // A running goroutine emits a task begin. - t.RawEvent(go122.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) - t.RawEvent(go122.EvGoCreate, nil, 0 /*timestamp delta*/, 1 /*go ID*/, 0, 0) + t.RawEvent(tracev2.EvEventBatch, nil, 1 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 5 /*batch length*/) + t.RawEvent(tracev2.EvGoCreate, nil, 0 /*timestamp delta*/, 1 /*go ID*/, 0, 0) // Write an invalid batch event for the next generation. - t.RawEvent(go122.EvEventBatch, nil, 2 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 50 /*batch length (invalid)*/) + t.RawEvent(tracev2.EvEventBatch, nil, 2 /*gen*/, 0 /*thread ID*/, 0 /*timestamp*/, 50 /*batch length (invalid)*/) // We should fail at the first issue, not the second one. t.ExpectFailure("expected a proc but didn't have one") diff --git a/src/internal/trace/testdata/generators/go122-go-create-without-running-g.go b/src/internal/trace/testdata/generators/go122-go-create-without-running-g.go index 217089975861fd..2e9b571d46ecca 100644 --- a/src/internal/trace/testdata/generators/go122-go-create-without-running-g.go +++ b/src/internal/trace/testdata/generators/go122-go-create-without-running-g.go @@ -13,12 +13,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -26,7 +27,7 @@ func gen(t *testgen.Trace) { // A goroutine gets created on a running P, then starts running. b0 := g1.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) b0.Event("GoCreate", trace.GoID(5), testgen.NoStack, testgen.NoStack) b0.Event("GoStart", trace.GoID(5), testgen.Seq(1)) b0.Event("GoStop", "whatever", testgen.NoStack) diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go index 4e7296983309b8..28d187c37e4782 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-ambiguous.go @@ -15,12 +15,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -31,8 +32,8 @@ func gen(t *testgen.Trace) { // One goroutine does a syscall without blocking, then another one where // it's P gets stolen. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) b0.Event("GoSyscallEnd") b0.Event("GoSyscallBegin", testgen.Seq(2), testgen.NoStack) @@ -40,7 +41,7 @@ func gen(t *testgen.Trace) { // A running goroutine steals proc 0. b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcStatus", trace.ProcID(2), tracev2.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), tracev2.GoRunning) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(3), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go index 1d7fe9c57c55d8..5350b197404fe1 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-bare-m.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -22,12 +23,12 @@ func gen(t *testgen.Trace) { // One goroutine is exiting with a syscall. It already // acquired a new P. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) + b0.Event("ProcStatus", trace.ProcID(1), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoSyscall) b0.Event("GoSyscallEndBlocked") // A bare M stole the goroutine's P at the generation boundary. b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcStatus", trace.ProcID(0), tracev2.ProcSyscallAbandoned) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go index a94b8f058d5922..f7611c5c0824c5 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc-bare-m.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -22,13 +23,13 @@ func gen(t *testgen.Trace) { // One goroutine is exiting with a syscall. It already // acquired a new P. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoSyscall) + b0.Event("ProcStatus", trace.ProcID(1), tracev2.ProcIdle) b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) b0.Event("GoSyscallEndBlocked") // A bare M stole the goroutine's P at the generation boundary. b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcStatus", trace.ProcID(0), tracev2.ProcSyscallAbandoned) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go index 04aef0644cdffb..521363b0942b99 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary-reacquire-new-proc.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -22,15 +23,15 @@ func gen(t *testgen.Trace) { // One goroutine is exiting with a syscall. It already // acquired a new P. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoSyscall) + b0.Event("ProcStatus", trace.ProcID(1), tracev2.ProcIdle) b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) b0.Event("GoSyscallEndBlocked") // A running goroutine stole P0 at the generation boundary. b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcStatus", trace.ProcID(2), tracev2.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), tracev2.GoRunning) + b1.Event("ProcStatus", trace.ProcID(0), tracev2.ProcSyscallAbandoned) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go index 769203ab4ae9dc..6c171c9cd1f89e 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-gen-boundary.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -22,14 +23,14 @@ func gen(t *testgen.Trace) { // One goroutine is exiting with a syscall. It already // acquired a new P. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoSyscall) + b0.Event("ProcStatus", trace.ProcID(1), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoSyscall) b0.Event("GoSyscallEndBlocked") // A running goroutine stole P0 at the generation boundary. b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b1.Event("ProcStatus", trace.ProcID(2), tracev2.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), tracev2.GoRunning) + b1.Event("ProcStatus", trace.ProcID(0), tracev2.ProcSyscallAbandoned) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go index c1c39569f8e7f7..18493dd5c383aa 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc-bare-m.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -21,9 +22,9 @@ func gen(t *testgen.Trace) { // One goroutine enters a syscall, grabs a P, and starts running. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("ProcStatus", trace.ProcID(1), tracev2.ProcIdle) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) b0.Event("GoSyscallEndBlocked") diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go index 217e4e6f962045..d4e6ed3e2a2ed4 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-reacquire-new-proc.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -21,16 +22,16 @@ func gen(t *testgen.Trace) { // One goroutine enters a syscall, grabs a P, and starts running. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(1), go122.ProcIdle) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("ProcStatus", trace.ProcID(1), tracev2.ProcIdle) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) b0.Event("ProcStart", trace.ProcID(1), testgen.Seq(1)) b0.Event("GoSyscallEndBlocked") // A running goroutine steals proc 0. b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcStatus", trace.ProcID(2), tracev2.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), tracev2.GoRunning) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go index a12f47177fb988..6dfb465b0a1819 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-self.go @@ -12,12 +12,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -28,8 +29,8 @@ func gen(t *testgen.Trace) { // A goroutine execute a syscall and steals its own P, then starts running // on that P. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) b0.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) b0.Event("ProcStart", trace.ProcID(0), testgen.Seq(3)) diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go index 34c7415cae613f..ac314a66478049 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple-bare-m.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -21,8 +22,8 @@ func gen(t *testgen.Trace) { // One goroutine enters a syscall. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) b0.Event("GoSyscallEndBlocked") diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go index 6304105af04cff..010272e5523c17 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-simple.go @@ -8,12 +8,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -21,14 +22,14 @@ func gen(t *testgen.Trace) { // One goroutine enters a syscall. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b0.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b0.Event("GoSyscallBegin", testgen.Seq(1), testgen.NoStack) b0.Event("GoSyscallEndBlocked") // A running goroutine steals proc 0. b1 := g.Batch(trace.ThreadID(1), 0) - b1.Event("ProcStatus", trace.ProcID(2), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), go122.GoRunning) + b1.Event("ProcStatus", trace.ProcID(2), tracev2.ProcRunning) + b1.Event("GoStatus", trace.GoID(2), trace.ThreadID(1), tracev2.GoRunning) b1.Event("ProcSteal", trace.ProcID(0), testgen.Seq(2), trace.ThreadID(0)) } diff --git a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go index ac84261f020593..410f9b7a089be8 100644 --- a/src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go +++ b/src/internal/trace/testdata/generators/go122-syscall-steal-proc-sitting-in-syscall.go @@ -9,12 +9,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -23,10 +24,10 @@ func gen(t *testgen.Trace) { // Steal proc from a goroutine that's been blocked // in a syscall the entire generation. b0 := g.Batch(trace.ThreadID(0), 0) - b0.Event("ProcStatus", trace.ProcID(0), go122.ProcSyscallAbandoned) + b0.Event("ProcStatus", trace.ProcID(0), tracev2.ProcSyscallAbandoned) b0.Event("ProcSteal", trace.ProcID(0), testgen.Seq(1), trace.ThreadID(1)) // Status event for a goroutine blocked in a syscall for the entire generation. bz := g.Batch(trace.NoThread, 0) - bz.Event("GoStatus", trace.GoID(1), trace.ThreadID(1), go122.GoSyscall) + bz.Event("GoStatus", trace.GoID(1), trace.ThreadID(1), tracev2.GoSyscall) } diff --git a/src/internal/trace/testdata/generators/go122-task-across-generations.go b/src/internal/trace/testdata/generators/go122-task-across-generations.go index 83b1bcdb5ef491..e8def318b403c5 100644 --- a/src/internal/trace/testdata/generators/go122-task-across-generations.go +++ b/src/internal/trace/testdata/generators/go122-task-across-generations.go @@ -14,12 +14,13 @@ package main import ( "internal/trace" - "internal/trace/event/go122" - testgen "internal/trace/internal/testgen/go122" + "internal/trace/internal/testgen" + "internal/trace/tracev2" + "internal/trace/version" ) func main() { - testgen.Main(gen) + testgen.Main(version.Go122, gen) } func gen(t *testgen.Trace) { @@ -27,15 +28,15 @@ func gen(t *testgen.Trace) { // A running goroutine emits a task begin. b1 := g1.Batch(trace.ThreadID(0), 0) - b1.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b1.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b1.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b1.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b1.Event("UserTaskBegin", trace.TaskID(2), trace.TaskID(0) /* 0 means no parent, not background */, "my task", testgen.NoStack) g2 := t.Generation(2) // That same goroutine emits a task end in the following generation. b2 := g2.Batch(trace.ThreadID(0), 5) - b2.Event("ProcStatus", trace.ProcID(0), go122.ProcRunning) - b2.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), go122.GoRunning) + b2.Event("ProcStatus", trace.ProcID(0), tracev2.ProcRunning) + b2.Event("GoStatus", trace.GoID(1), trace.ThreadID(0), tracev2.GoRunning) b2.Event("UserTaskEnd", trace.TaskID(2), testgen.NoStack) } diff --git a/src/internal/trace/testtrace/validation.go b/src/internal/trace/testtrace/validation.go index 59ff19e6106e9b..f61f7a3ffa6031 100644 --- a/src/internal/trace/testtrace/validation.go +++ b/src/internal/trace/testtrace/validation.go @@ -14,14 +14,14 @@ import ( // Validator is a type used for validating a stream of trace.Events. type Validator struct { - lastTs trace.Time - gs map[trace.GoID]*goState - ps map[trace.ProcID]*procState - ms map[trace.ThreadID]*schedContext - ranges map[trace.ResourceID][]string - tasks map[trace.TaskID]string - seenSync bool - Go121 bool + lastTs trace.Time + gs map[trace.GoID]*goState + ps map[trace.ProcID]*procState + ms map[trace.ThreadID]*schedContext + ranges map[trace.ResourceID][]string + tasks map[trace.TaskID]string + nSync int + Go121 bool } type schedContext struct { @@ -60,7 +60,7 @@ func (v *Validator) Event(ev trace.Event) error { // Validate timestamp order. if v.lastTs != 0 { if ev.Time() <= v.lastTs { - e.Errorf("timestamp out-of-order for %+v", ev) + e.Errorf("timestamp out-of-order (want > %v) for %+v", v.lastTs, ev) } else { v.lastTs = ev.Time() } @@ -73,8 +73,11 @@ func (v *Validator) Event(ev trace.Event) error { switch ev.Kind() { case trace.EventSync: - // Just record that we've seen a Sync at some point. - v.seenSync = true + s := ev.Sync() + if s.N != v.nSync+1 { + e.Errorf("sync count is not sequential: expected %d, got %d", v.nSync+1, s.N) + } + v.nSync = s.N case trace.EventMetric: m := ev.Metric() if !strings.Contains(m.Name, ":") { @@ -88,7 +91,7 @@ func (v *Validator) Event(ev trace.Event) error { switch m.Value.Kind() { case trace.ValueUint64: // Just make sure it doesn't panic. - _ = m.Value.Uint64() + _ = m.Value.ToUint64() } case trace.EventLabel: l := ev.Label() @@ -140,7 +143,7 @@ func (v *Validator) Event(ev trace.Event) error { if new == trace.GoUndetermined { e.Errorf("transition to undetermined state for goroutine %d", id) } - if v.seenSync && old == trace.GoUndetermined { + if v.nSync > 1 && old == trace.GoUndetermined { e.Errorf("undetermined goroutine %d after first global sync", id) } if new == trace.GoNotExist && v.hasAnyRange(trace.MakeResourceID(id)) { @@ -193,7 +196,7 @@ func (v *Validator) Event(ev trace.Event) error { if new == trace.ProcUndetermined { e.Errorf("transition to undetermined state for proc %d", id) } - if v.seenSync && old == trace.ProcUndetermined { + if v.nSync > 1 && old == trace.ProcUndetermined { e.Errorf("undetermined proc %d after first global sync", id) } if new == trace.ProcNotExist && v.hasAnyRange(trace.MakeResourceID(id)) { diff --git a/src/internal/trace/trace_test.go b/src/internal/trace/trace_test.go index facac47eef0604..4ff87c1ede6ac6 100644 --- a/src/internal/trace/trace_test.go +++ b/src/internal/trace/trace_test.go @@ -573,6 +573,11 @@ func testTraceProg(t *testing.T, progName string, extra func(t *testing.T, trace onBuilder := testenv.Builder() != "" onOldBuilder := !strings.Contains(testenv.Builder(), "gotip") && !strings.Contains(testenv.Builder(), "go1") + if progName == "cgo-callback.go" && onBuilder && !onOldBuilder && + runtime.GOOS == "freebsd" && runtime.GOARCH == "amd64" && race.Enabled { + t.Skip("test fails on freebsd-amd64-race in LUCI; see go.dev/issue/71556") + } + testPath := filepath.Join("./testdata/testprog", progName) testName := progName runTest := func(t *testing.T, stress bool, extraGODEBUG string) { diff --git a/src/internal/trace/oldtrace.go b/src/internal/trace/tracev1.go similarity index 72% rename from src/internal/trace/oldtrace.go rename to src/internal/trace/tracev1.go index c49f8c7474d49a..667d7be1cd1b4b 100644 --- a/src/internal/trace/oldtrace.go +++ b/src/internal/trace/tracev1.go @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements conversion from old (Go 1.11–Go 1.21) traces to the Go -// 1.22 format. +// This file implements conversion from v1 (Go 1.11–Go 1.21) traces to the v2 +// format (Go 1.22+). // -// Most events have direct equivalents in 1.22, at worst requiring arguments to +// Most events have direct equivalents in v2, at worst requiring arguments to // be reordered. Some events, such as GoWaiting need to look ahead for follow-up // events to determine the correct translation. GoSyscall, which is an // instantaneous event, gets turned into a 1 ns long pair of @@ -13,11 +13,11 @@ // emit a GoSyscallStart+GoSyscallEndBlocked pair with the correct duration // (i.e. starting at the original GoSyscall). // -// The resulting trace treats the old trace as a single, large generation, +// The resulting trace treats the trace v1 as a single, large generation, // sharing a single evTable for all events. // // We use a new (compared to what was used for 'go tool trace' in earlier -// versions of Go) parser for old traces that is optimized for speed, low memory +// versions of Go) parser for v1 traces that is optimized for speed, low memory // usage, and minimal GC pressure. It allocates events in batches so that even // though we have to load the entire trace into memory, the conversion process // shouldn't result in a doubling of memory usage, even if all converted events @@ -30,18 +30,17 @@ package trace import ( "errors" "fmt" - "internal/trace/event" - "internal/trace/event/go122" - "internal/trace/internal/oldtrace" + "internal/trace/internal/tracev1" + "internal/trace/tracev2" "io" ) -type oldTraceConverter struct { - trace oldtrace.Trace +type traceV1Converter struct { + trace tracev1.Trace evt *evTable preInit bool createdPreInit map[GoID]struct{} - events oldtrace.Events + events tracev1.Events extra []Event extraArr [3]Event tasks map[TaskID]taskState @@ -91,7 +90,7 @@ const ( sLast ) -func (it *oldTraceConverter) init(pr oldtrace.Trace) error { +func (it *traceV1Converter) init(pr tracev1.Trace) error { it.trace = pr it.preInit = true it.createdPreInit = make(map[GoID]struct{}) @@ -105,7 +104,7 @@ func (it *oldTraceConverter) init(pr oldtrace.Trace) error { evt := it.evt - // Convert from oldtracer's Strings map to our dataTable. + // Convert from trace v1's Strings map to our dataTable. var max uint64 for id, s := range pr.Strings { evt.strings.insert(stringID(id), s) @@ -115,7 +114,7 @@ func (it *oldTraceConverter) init(pr oldtrace.Trace) error { } pr.Strings = nil - // Add all strings used for UserLog. In the old trace format, these were + // Add all strings used for UserLog. In the trace v1 format, these were // stored inline and didn't have IDs. We generate IDs for them. if max+uint64(len(pr.InlineStrings)) < max { return errors.New("trace contains too many strings") @@ -187,7 +186,7 @@ func (it *oldTraceConverter) init(pr oldtrace.Trace) error { } // OPT(dh): if we could share the frame type between this package and - // oldtrace we wouldn't have to copy the map. + // tracev1 we wouldn't have to copy the map. for pc, f := range pr.PCs { evt.pcs[pc] = frame{ pc: pc, @@ -204,7 +203,7 @@ func (it *oldTraceConverter) init(pr oldtrace.Trace) error { // next returns the next event, io.EOF if there are no more events, or a // descriptive error for invalid events. -func (it *oldTraceConverter) next() (Event, error) { +func (it *traceV1Converter) next() (Event, error) { if len(it.extra) > 0 { ev := it.extra[0] it.extra = it.extra[1:] @@ -245,21 +244,21 @@ func (it *oldTraceConverter) next() (Event, error) { var errSkip = errors.New("skip event") -// convertEvent converts an event from the old trace format to zero or more +// convertEvent converts an event from the trace v1 format to zero or more // events in the new format. Most events translate 1 to 1. Some events don't // result in an event right away, in which case convertEvent returns errSkip. // Some events result in more than one new event; in this case, convertEvent // returns the first event and stores additional events in it.extra. When -// encountering events that oldtrace shouldn't be able to emit, ocnvertEvent +// encountering events that tracev1 shouldn't be able to emit, ocnvertEvent // returns a descriptive error. -func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR error) { - var mappedType event.Type +func (it *traceV1Converter) convertEvent(ev *tracev1.Event) (OUT Event, ERR error) { + var mappedType tracev2.EventType var mappedArgs timedEventArgs copy(mappedArgs[:], ev.Args[:]) switch ev.Type { - case oldtrace.EvGomaxprocs: - mappedType = go122.EvProcsChange + case tracev1.EvGomaxprocs: + mappedType = tracev2.EvProcsChange if it.preInit { // The first EvGomaxprocs signals the end of trace initialization. At this point we've seen // all goroutines that already existed at trace begin. @@ -277,65 +276,65 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er }, table: it.evt, base: baseEvent{ - typ: go122.EvGoStatus, + typ: tracev2.EvGoStatus, time: Time(ev.Ts), - args: timedEventArgs{uint64(gid), ^uint64(0), uint64(go122.GoRunnable)}, + args: timedEventArgs{uint64(gid), ^uint64(0), uint64(tracev2.GoRunnable)}, }, }) } it.createdPreInit = nil return Event{}, errSkip } - case oldtrace.EvProcStart: + case tracev1.EvProcStart: it.procMs[ProcID(ev.P)] = ThreadID(ev.Args[0]) if _, ok := it.seenProcs[ProcID(ev.P)]; ok { - mappedType = go122.EvProcStart + mappedType = tracev2.EvProcStart mappedArgs = timedEventArgs{uint64(ev.P)} } else { it.seenProcs[ProcID(ev.P)] = struct{}{} - mappedType = go122.EvProcStatus - mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcRunning)} + mappedType = tracev2.EvProcStatus + mappedArgs = timedEventArgs{uint64(ev.P), uint64(tracev2.ProcRunning)} } - case oldtrace.EvProcStop: + case tracev1.EvProcStop: if _, ok := it.seenProcs[ProcID(ev.P)]; ok { - mappedType = go122.EvProcStop + mappedType = tracev2.EvProcStop mappedArgs = timedEventArgs{uint64(ev.P)} } else { it.seenProcs[ProcID(ev.P)] = struct{}{} - mappedType = go122.EvProcStatus - mappedArgs = timedEventArgs{uint64(ev.P), uint64(go122.ProcIdle)} + mappedType = tracev2.EvProcStatus + mappedArgs = timedEventArgs{uint64(ev.P), uint64(tracev2.ProcIdle)} } - case oldtrace.EvGCStart: - mappedType = go122.EvGCBegin - case oldtrace.EvGCDone: - mappedType = go122.EvGCEnd - case oldtrace.EvSTWStart: + case tracev1.EvGCStart: + mappedType = tracev2.EvGCBegin + case tracev1.EvGCDone: + mappedType = tracev2.EvGCEnd + case tracev1.EvSTWStart: sid := it.builtinToStringID[sSTWUnknown+it.trace.STWReason(ev.Args[0])] it.lastStwReason = sid - mappedType = go122.EvSTWBegin + mappedType = tracev2.EvSTWBegin mappedArgs = timedEventArgs{uint64(sid)} - case oldtrace.EvSTWDone: - mappedType = go122.EvSTWEnd + case tracev1.EvSTWDone: + mappedType = tracev2.EvSTWEnd mappedArgs = timedEventArgs{it.lastStwReason} - case oldtrace.EvGCSweepStart: - mappedType = go122.EvGCSweepBegin - case oldtrace.EvGCSweepDone: - mappedType = go122.EvGCSweepEnd - case oldtrace.EvGoCreate: + case tracev1.EvGCSweepStart: + mappedType = tracev2.EvGCSweepBegin + case tracev1.EvGCSweepDone: + mappedType = tracev2.EvGCSweepEnd + case tracev1.EvGoCreate: if it.preInit { it.createdPreInit[GoID(ev.Args[0])] = struct{}{} return Event{}, errSkip } - mappedType = go122.EvGoCreate - case oldtrace.EvGoStart: + mappedType = tracev2.EvGoCreate + case tracev1.EvGoStart: if it.preInit { - mappedType = go122.EvGoStatus - mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoRunning)} + mappedType = tracev2.EvGoStatus + mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(tracev2.GoRunning)} delete(it.createdPreInit, GoID(ev.Args[0])) } else { - mappedType = go122.EvGoStart + mappedType = tracev2.EvGoStart } - case oldtrace.EvGoStartLabel: + case tracev1.EvGoStartLabel: it.extra = []Event{{ ctx: schedCtx{ G: GoID(ev.G), @@ -344,7 +343,7 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er }, table: it.evt, base: baseEvent{ - typ: go122.EvGoLabel, + typ: tracev2.EvGoLabel, time: Time(ev.Ts), args: timedEventArgs{ev.Args[2]}, }, @@ -357,69 +356,69 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er }, table: it.evt, base: baseEvent{ - typ: go122.EvGoStart, + typ: tracev2.EvGoStart, time: Time(ev.Ts), args: mappedArgs, }, }, nil - case oldtrace.EvGoEnd: - mappedType = go122.EvGoDestroy - case oldtrace.EvGoStop: - mappedType = go122.EvGoBlock + case tracev1.EvGoEnd: + mappedType = tracev2.EvGoDestroy + case tracev1.EvGoStop: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sForever]), uint64(ev.StkID)} - case oldtrace.EvGoSched: - mappedType = go122.EvGoStop + case tracev1.EvGoSched: + mappedType = tracev2.EvGoStop mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sGosched]), uint64(ev.StkID)} - case oldtrace.EvGoPreempt: - mappedType = go122.EvGoStop + case tracev1.EvGoPreempt: + mappedType = tracev2.EvGoStop mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sPreempted]), uint64(ev.StkID)} - case oldtrace.EvGoSleep: - mappedType = go122.EvGoBlock + case tracev1.EvGoSleep: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSleep]), uint64(ev.StkID)} - case oldtrace.EvGoBlock: - mappedType = go122.EvGoBlock + case tracev1.EvGoBlock: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sEmpty]), uint64(ev.StkID)} - case oldtrace.EvGoUnblock: - mappedType = go122.EvGoUnblock - case oldtrace.EvGoBlockSend: - mappedType = go122.EvGoBlock + case tracev1.EvGoUnblock: + mappedType = tracev2.EvGoUnblock + case tracev1.EvGoBlockSend: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanSend]), uint64(ev.StkID)} - case oldtrace.EvGoBlockRecv: - mappedType = go122.EvGoBlock + case tracev1.EvGoBlockRecv: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sChanRecv]), uint64(ev.StkID)} - case oldtrace.EvGoBlockSelect: - mappedType = go122.EvGoBlock + case tracev1.EvGoBlockSelect: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSelect]), uint64(ev.StkID)} - case oldtrace.EvGoBlockSync: - mappedType = go122.EvGoBlock + case tracev1.EvGoBlockSync: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSync]), uint64(ev.StkID)} - case oldtrace.EvGoBlockCond: - mappedType = go122.EvGoBlock + case tracev1.EvGoBlockCond: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sSyncCond]), uint64(ev.StkID)} - case oldtrace.EvGoBlockNet: - mappedType = go122.EvGoBlock + case tracev1.EvGoBlockNet: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sNetwork]), uint64(ev.StkID)} - case oldtrace.EvGoBlockGC: - mappedType = go122.EvGoBlock + case tracev1.EvGoBlockGC: + mappedType = tracev2.EvGoBlock mappedArgs = timedEventArgs{uint64(it.builtinToStringID[sMarkAssistWait]), uint64(ev.StkID)} - case oldtrace.EvGoSysCall: + case tracev1.EvGoSysCall: // Look for the next event for the same G to determine if the syscall // blocked. blocked := false - it.events.All()(func(nev *oldtrace.Event) bool { + it.events.All()(func(nev *tracev1.Event) bool { if nev.G != ev.G { return true } // After an EvGoSysCall, the next event on the same G will either be // EvGoSysBlock to denote a blocking syscall, or some other event // (or the end of the trace) if the syscall didn't block. - if nev.Type == oldtrace.EvGoSysBlock { + if nev.Type == tracev1.EvGoSysBlock { blocked = true } return false }) if blocked { - mappedType = go122.EvGoSyscallBegin + mappedType = tracev2.EvGoSyscallBegin mappedArgs = timedEventArgs{1: uint64(ev.StkID)} } else { // Convert the old instantaneous syscall event to a pair of syscall @@ -433,7 +432,7 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er }, table: it.evt, base: baseEvent{ - typ: go122.EvGoSyscallBegin, + typ: tracev2.EvGoSyscallBegin, time: Time(ev.Ts), args: timedEventArgs{1: uint64(ev.StkID)}, }, @@ -443,7 +442,7 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er ctx: out1.ctx, table: it.evt, base: baseEvent{ - typ: go122.EvGoSyscallEnd, + typ: tracev2.EvGoSyscallEnd, time: Time(ev.Ts + 1), args: timedEventArgs{}, }, @@ -453,31 +452,31 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er return out1, nil } - case oldtrace.EvGoSysExit: - mappedType = go122.EvGoSyscallEndBlocked - case oldtrace.EvGoSysBlock: + case tracev1.EvGoSysExit: + mappedType = tracev2.EvGoSyscallEndBlocked + case tracev1.EvGoSysBlock: return Event{}, errSkip - case oldtrace.EvGoWaiting: - mappedType = go122.EvGoStatus - mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoWaiting)} + case tracev1.EvGoWaiting: + mappedType = tracev2.EvGoStatus + mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(tracev2.GoWaiting)} delete(it.createdPreInit, GoID(ev.Args[0])) - case oldtrace.EvGoInSyscall: - mappedType = go122.EvGoStatus + case tracev1.EvGoInSyscall: + mappedType = tracev2.EvGoStatus // In the new tracer, GoStatus with GoSyscall knows what thread the - // syscall is on. In the old tracer, EvGoInSyscall doesn't contain that + // syscall is on. In trace v1, EvGoInSyscall doesn't contain that // information and all we can do here is specify NoThread. - mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(go122.GoSyscall)} + mappedArgs = timedEventArgs{ev.Args[0], ^uint64(0), uint64(tracev2.GoSyscall)} delete(it.createdPreInit, GoID(ev.Args[0])) - case oldtrace.EvHeapAlloc: - mappedType = go122.EvHeapAlloc - case oldtrace.EvHeapGoal: - mappedType = go122.EvHeapGoal - case oldtrace.EvGCMarkAssistStart: - mappedType = go122.EvGCMarkAssistBegin - case oldtrace.EvGCMarkAssistDone: - mappedType = go122.EvGCMarkAssistEnd - case oldtrace.EvUserTaskCreate: - mappedType = go122.EvUserTaskBegin + case tracev1.EvHeapAlloc: + mappedType = tracev2.EvHeapAlloc + case tracev1.EvHeapGoal: + mappedType = tracev2.EvHeapGoal + case tracev1.EvGCMarkAssistStart: + mappedType = tracev2.EvGCMarkAssistBegin + case tracev1.EvGCMarkAssistDone: + mappedType = tracev2.EvGCMarkAssistEnd + case tracev1.EvUserTaskCreate: + mappedType = tracev2.EvUserTaskBegin parent := ev.Args[1] if parent == 0 { parent = uint64(NoTask) @@ -485,8 +484,8 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er mappedArgs = timedEventArgs{ev.Args[0], parent, ev.Args[2], uint64(ev.StkID)} name, _ := it.evt.strings.get(stringID(ev.Args[2])) it.tasks[TaskID(ev.Args[0])] = taskState{name: name, parentID: TaskID(ev.Args[1])} - case oldtrace.EvUserTaskEnd: - mappedType = go122.EvUserTaskEnd + case tracev1.EvUserTaskEnd: + mappedType = tracev2.EvUserTaskEnd // Event.Task expects the parent and name to be smuggled in extra args // and as extra strings. ts, ok := it.tasks[TaskID(ev.Args[0])] @@ -501,19 +500,19 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er } else { mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], uint64(NoTask), uint64(it.evt.addExtraString(""))} } - case oldtrace.EvUserRegion: + case tracev1.EvUserRegion: switch ev.Args[1] { case 0: // start - mappedType = go122.EvUserRegionBegin + mappedType = tracev2.EvUserRegionBegin case 1: // end - mappedType = go122.EvUserRegionEnd + mappedType = tracev2.EvUserRegionEnd } mappedArgs = timedEventArgs{ev.Args[0], ev.Args[2], uint64(ev.StkID)} - case oldtrace.EvUserLog: - mappedType = go122.EvUserLog + case tracev1.EvUserLog: + mappedType = tracev2.EvUserLog mappedArgs = timedEventArgs{ev.Args[0], ev.Args[1], it.inlineToStringID[ev.Args[3]], uint64(ev.StkID)} - case oldtrace.EvCPUSample: - mappedType = go122.EvCPUSample + case tracev1.EvCPUSample: + mappedType = tracev2.EvCPUSample // When emitted by the Go 1.22 tracer, CPU samples have 5 arguments: // timestamp, M, P, G, stack. However, after they get turned into Event, // they have the arguments stack, M, P, G. @@ -524,19 +523,19 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er return Event{}, fmt.Errorf("unexpected event type %v", ev.Type) } - if oldtrace.EventDescriptions[ev.Type].Stack { - if stackIDs := go122.Specs()[mappedType].StackIDs; len(stackIDs) > 0 { + if tracev1.EventDescriptions[ev.Type].Stack { + if stackIDs := tracev2.Specs()[mappedType].StackIDs; len(stackIDs) > 0 { mappedArgs[stackIDs[0]-1] = uint64(ev.StkID) } } m := NoThread - if ev.P != -1 && ev.Type != oldtrace.EvCPUSample { + if ev.P != -1 && ev.Type != tracev1.EvCPUSample { if t, ok := it.procMs[ProcID(ev.P)]; ok { m = ThreadID(t) } } - if ev.Type == oldtrace.EvProcStop { + if ev.Type == tracev1.EvProcStop { delete(it.procMs, ProcID(ev.P)) } g := GoID(ev.G) @@ -559,10 +558,10 @@ func (it *oldTraceConverter) convertEvent(ev *oldtrace.Event) (OUT Event, ERR er return out, nil } -// convertOldFormat takes a fully loaded trace in the old trace format and +// convertV1Trace takes a fully loaded trace in the v1 trace format and // returns an iterator over events in the new format. -func convertOldFormat(pr oldtrace.Trace) *oldTraceConverter { - it := &oldTraceConverter{} +func convertV1Trace(pr tracev1.Trace) *traceV1Converter { + it := &traceV1Converter{} it.init(pr) return it } diff --git a/src/internal/trace/oldtrace_test.go b/src/internal/trace/tracev1_test.go similarity index 88% rename from src/internal/trace/oldtrace_test.go rename to src/internal/trace/tracev1_test.go index f812d5ef840700..008c7535c9f35f 100644 --- a/src/internal/trace/oldtrace_test.go +++ b/src/internal/trace/tracev1_test.go @@ -13,15 +13,15 @@ import ( "testing" ) -func TestOldtrace(t *testing.T) { - traces, err := filepath.Glob("./internal/oldtrace/testdata/*_good") +func TestTraceV1(t *testing.T) { + traces, err := filepath.Glob("./internal/tracev1/testdata/*_good") if err != nil { t.Fatalf("failed to glob for tests: %s", err) } var testedUserRegions bool for _, p := range traces { p := p - testName, err := filepath.Rel("./internal/oldtrace/testdata", p) + testName, err := filepath.Rel("./internal/tracev1/testdata", p) if err != nil { t.Fatalf("failed to relativize testdata path: %s", err) } @@ -54,9 +54,9 @@ func TestOldtrace(t *testing.T) { if testName == "user_task_region_1_21_good" { testedUserRegions = true validRegions := map[string]struct{}{ - "post-existing region": struct{}{}, - "region0": struct{}{}, - "region1": struct{}{}, + "post-existing region": {}, + "region0": {}, + "region1": {}, } // Check that we correctly convert user regions. These // strings were generated by diff --git a/src/internal/trace/tracev2/EXPERIMENTS.md b/src/internal/trace/tracev2/EXPERIMENTS.md new file mode 100644 index 00000000000000..fedf7bdbdc8db7 --- /dev/null +++ b/src/internal/trace/tracev2/EXPERIMENTS.md @@ -0,0 +1,101 @@ +# Trace experiments + +Execution traces allow for trialing new events on an experimental basis via +trace experiments. +This document is a guide that explains how you can define your own trace +experiments. + +Note that if you're just trying to do some debugging or perform some light +instrumentation, then a trace experiment is way overkill. +Use `runtime/trace.Log` instead. +Even if you're just trying to create a proof-of-concept for a low-frequency +event, `runtime/trace.Log` will probably be easier overall if you can make +it work. + +Consider a trace experiment if: +- The volume of new trace events will be relatively high, and so the events + would benefit from a more compact representation (creating new tables to + deduplicate data, taking advantage of the varint representation, etc.). +- It's not safe to call `runtime/trace.Log` (or its runtime equivalent) in + the contexts you want to generate an event (for example, for events about + timers). + +## Defining a new experiment + +To define a new experiment, modify `internal/trace/tracev2` to define a +new `Experiment` enum value. + +An experiment consists of two parts: timed events and experimental batches. +Timed events are events like any other and follow the same format. +They are easier to order and require less work to make use of. +Experimental batches are essentially bags of bytes that correspond to +an entire trace generation. +What they contain and how they're interpreted is totally up to you, but +they're most often useful for tables that your other events can refer into. +For example, the AllocFree experiment uses them to store type information +that allocation events can refer to. + +### Defining new events + +1. Define your new experiment event types (by convention, experimental events + types start at ID 127, so look for the `const` block defining events + starting there). +2. Describe your new events in `specs`. + Use the documentation for `Spec` to write your new specs, and check your + work by running the tests in the `internal/trace/tracev2` package. + If you wish for your event argument to be interpreted in a particular + way, follow the naming convention in + `src/internal/trace/tracev2/spec.go`. + For example, if you intend to emit a string argument, make sure the + argument name has the suffix `string`. +3. Add ordering and validation logic for your new events to + `src/internal/trace/order.go` by listing handlers for those events in + the `orderingDispatch` table. + If your events are always emitted in a regular user goroutine context, + then the handler should be trivial and just validate the scheduling + context to match userGoReqs. + If it's more complicated, see `(*ordering).advanceAllocFree` for a + slightly more complicated example that handles events from a larger + variety of execution environments. + If you need to encode a partial ordering, look toward the scheduler + events (names beginning with `Go`) or just ask someone for help. +4. Add your new events to the `tracev2Type2Kind` table in + `src/internal/trace/event.go`. + +## Emitting data + +### Emitting your new events + +1. Define helper methods on `runtime.traceEventWriter` for emitting your + events. +2. Instrument the runtime with calls to these helper methods. + Make sure to call `traceAcquire` and `traceRelease` around the operation + your event represents, otherwise it will not be emitted atomically with + that operation completing, resulting in a potentially misleading trace. + +### Emitting experimental batches + +To emit experimental batches, use the `runtime.unsafeTraceExpWriter` to +write experimental batches associated with your experiment. +Heed the warnings and make sure that while you write them, the trace +generation cannot advance. +Note that each experiment can only have one distinguishable set of +batches. + +## Recovering experimental data + +### Recovering experimental events from the trace + +Experimental events will appear in the event stream as an event with the +`EventExperimental` `Kind`. +Use the `Experimental` method to collect the raw data inserted into the +trace. +It's essentially up to you to interpret the event from here. +I recommend writing a thin wrapper API to present a cleaner interface if you +so desire. + +### Recovering experimental batches + +Parse out all the experimental batches from `Sync` events as they come. +These experimental batches are all for the same generation as all the +experimental events up until the next `Sync` event. diff --git a/src/internal/trace/tracev2/doc.go b/src/internal/trace/tracev2/doc.go new file mode 100644 index 00000000000000..725fc0821d0fd7 --- /dev/null +++ b/src/internal/trace/tracev2/doc.go @@ -0,0 +1,11 @@ +// Copyright 2025 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. + +/* +Package tracev2 contains definitions for the v2 execution trace wire format. + +These definitions are shared between the trace parser and the runtime, so it +must not depend on any package that depends on the runtime (most packages). +*/ +package tracev2 diff --git a/src/internal/trace/event/go122/event.go b/src/internal/trace/tracev2/events.go similarity index 78% rename from src/internal/trace/event/go122/event.go rename to src/internal/trace/tracev2/events.go index f6075e3ed51413..2f3581ab5b7ccb 100644 --- a/src/internal/trace/event/go122/event.go +++ b/src/internal/trace/tracev2/events.go @@ -2,15 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package go122 - -import ( - "fmt" - "internal/trace/event" -) +package tracev2 +// Event types in the trace, args are given in square brackets. +// +// Naming scheme: +// - Time range event pairs have suffixes "Begin" and "End". +// - "Start", "Stop", "Create", "Destroy", "Block", "Unblock" +// are suffixes reserved for scheduling resources. +// +// NOTE: If you add an event type, make sure you also update all +// tables in this file! const ( - EvNone event.Type = iota // unused + EvNone EventType = iota // unused // Structural events. EvEventBatch // start of per-M batch of events [generation, M ID, timestamp, batch length] @@ -78,17 +82,34 @@ const ( // Batch event for an experimental batch with a custom format. Added in Go 1.23. EvExperimentalBatch // start of extra data [experiment ID, generation, M ID, timestamp, batch length, batch data...] + + NumEvents ) +func (ev EventType) Experimental() bool { + return ev > MaxEvent && ev < MaxExperimentalEvent +} + // Experiments. const ( // AllocFree is the alloc-free events experiment. - AllocFree event.Experiment = 1 + iota + AllocFree Experiment = 1 + iota + + NumExperiments ) +func Experiments() []string { + return experiments[:] +} + +var experiments = [...]string{ + NoExperiment: "None", + AllocFree: "AllocFree", +} + // Experimental events. const ( - _ event.Type = 127 + iota + MaxEvent EventType = 127 + iota // Experimental events for AllocFree. @@ -106,262 +127,262 @@ const ( EvGoroutineStack // stack exists [timestamp, id, order] EvGoroutineStackAlloc // stack alloc [timestamp, id, order] EvGoroutineStackFree // stack free [timestamp, id] + + MaxExperimentalEvent ) -// EventString returns the name of a Go 1.22 event. -func EventString(typ event.Type) string { - if int(typ) < len(specs) { - return specs[typ].Name - } - return fmt.Sprintf("Invalid(%d)", typ) -} +const NumExperimentalEvents = MaxExperimentalEvent - MaxEvent -func Specs() []event.Spec { +// MaxTimedEventArgs is the maximum number of arguments for timed events. +const MaxTimedEventArgs = 5 + +func Specs() []EventSpec { return specs[:] } -var specs = [...]event.Spec{ +var specs = [...]EventSpec{ // "Structural" Events. - EvEventBatch: event.Spec{ + EvEventBatch: { Name: "EventBatch", Args: []string{"gen", "m", "time", "size"}, }, - EvStacks: event.Spec{ + EvStacks: { Name: "Stacks", }, - EvStack: event.Spec{ + EvStack: { Name: "Stack", Args: []string{"id", "nframes"}, IsStack: true, }, - EvStrings: event.Spec{ + EvStrings: { Name: "Strings", }, - EvString: event.Spec{ + EvString: { Name: "String", Args: []string{"id"}, HasData: true, }, - EvCPUSamples: event.Spec{ + EvCPUSamples: { Name: "CPUSamples", }, - EvCPUSample: event.Spec{ + EvCPUSample: { Name: "CPUSample", Args: []string{"time", "m", "p", "g", "stack"}, // N.B. There's clearly a timestamp here, but these Events // are special in that they don't appear in the regular // M streams. + StackIDs: []int{4}, }, - EvFrequency: event.Spec{ + EvFrequency: { Name: "Frequency", Args: []string{"freq"}, }, - EvExperimentalBatch: event.Spec{ + EvExperimentalBatch: { Name: "ExperimentalBatch", Args: []string{"exp", "gen", "m", "time"}, HasData: true, // Easier to represent for raw readers. }, // "Timed" Events. - EvProcsChange: event.Spec{ + EvProcsChange: { Name: "ProcsChange", Args: []string{"dt", "procs_value", "stack"}, IsTimedEvent: true, StackIDs: []int{2}, }, - EvProcStart: event.Spec{ + EvProcStart: { Name: "ProcStart", Args: []string{"dt", "p", "p_seq"}, IsTimedEvent: true, }, - EvProcStop: event.Spec{ + EvProcStop: { Name: "ProcStop", Args: []string{"dt"}, IsTimedEvent: true, }, - EvProcSteal: event.Spec{ + EvProcSteal: { Name: "ProcSteal", Args: []string{"dt", "p", "p_seq", "m"}, IsTimedEvent: true, }, - EvProcStatus: event.Spec{ + EvProcStatus: { Name: "ProcStatus", Args: []string{"dt", "p", "pstatus"}, IsTimedEvent: true, }, - EvGoCreate: event.Spec{ + EvGoCreate: { Name: "GoCreate", Args: []string{"dt", "new_g", "new_stack", "stack"}, IsTimedEvent: true, StackIDs: []int{3, 2}, }, - EvGoCreateSyscall: event.Spec{ + EvGoCreateSyscall: { Name: "GoCreateSyscall", Args: []string{"dt", "new_g"}, IsTimedEvent: true, }, - EvGoStart: event.Spec{ + EvGoStart: { Name: "GoStart", Args: []string{"dt", "g", "g_seq"}, IsTimedEvent: true, }, - EvGoDestroy: event.Spec{ + EvGoDestroy: { Name: "GoDestroy", Args: []string{"dt"}, IsTimedEvent: true, }, - EvGoDestroySyscall: event.Spec{ + EvGoDestroySyscall: { Name: "GoDestroySyscall", Args: []string{"dt"}, IsTimedEvent: true, }, - EvGoStop: event.Spec{ + EvGoStop: { Name: "GoStop", Args: []string{"dt", "reason_string", "stack"}, IsTimedEvent: true, StackIDs: []int{2}, StringIDs: []int{1}, }, - EvGoBlock: event.Spec{ + EvGoBlock: { Name: "GoBlock", Args: []string{"dt", "reason_string", "stack"}, IsTimedEvent: true, StackIDs: []int{2}, StringIDs: []int{1}, }, - EvGoUnblock: event.Spec{ + EvGoUnblock: { Name: "GoUnblock", Args: []string{"dt", "g", "g_seq", "stack"}, IsTimedEvent: true, StackIDs: []int{3}, }, - EvGoSyscallBegin: event.Spec{ + EvGoSyscallBegin: { Name: "GoSyscallBegin", Args: []string{"dt", "p_seq", "stack"}, IsTimedEvent: true, StackIDs: []int{2}, }, - EvGoSyscallEnd: event.Spec{ + EvGoSyscallEnd: { Name: "GoSyscallEnd", Args: []string{"dt"}, StartEv: EvGoSyscallBegin, IsTimedEvent: true, }, - EvGoSyscallEndBlocked: event.Spec{ + EvGoSyscallEndBlocked: { Name: "GoSyscallEndBlocked", Args: []string{"dt"}, StartEv: EvGoSyscallBegin, IsTimedEvent: true, }, - EvGoStatus: event.Spec{ + EvGoStatus: { Name: "GoStatus", Args: []string{"dt", "g", "m", "gstatus"}, IsTimedEvent: true, }, - EvSTWBegin: event.Spec{ + EvSTWBegin: { Name: "STWBegin", Args: []string{"dt", "kind_string", "stack"}, IsTimedEvent: true, StackIDs: []int{2}, StringIDs: []int{1}, }, - EvSTWEnd: event.Spec{ + EvSTWEnd: { Name: "STWEnd", Args: []string{"dt"}, StartEv: EvSTWBegin, IsTimedEvent: true, }, - EvGCActive: event.Spec{ + EvGCActive: { Name: "GCActive", Args: []string{"dt", "gc_seq"}, IsTimedEvent: true, StartEv: EvGCBegin, }, - EvGCBegin: event.Spec{ + EvGCBegin: { Name: "GCBegin", Args: []string{"dt", "gc_seq", "stack"}, IsTimedEvent: true, StackIDs: []int{2}, }, - EvGCEnd: event.Spec{ + EvGCEnd: { Name: "GCEnd", Args: []string{"dt", "gc_seq"}, StartEv: EvGCBegin, IsTimedEvent: true, }, - EvGCSweepActive: event.Spec{ + EvGCSweepActive: { Name: "GCSweepActive", Args: []string{"dt", "p"}, StartEv: EvGCSweepBegin, IsTimedEvent: true, }, - EvGCSweepBegin: event.Spec{ + EvGCSweepBegin: { Name: "GCSweepBegin", Args: []string{"dt", "stack"}, IsTimedEvent: true, StackIDs: []int{1}, }, - EvGCSweepEnd: event.Spec{ + EvGCSweepEnd: { Name: "GCSweepEnd", Args: []string{"dt", "swept_value", "reclaimed_value"}, StartEv: EvGCSweepBegin, IsTimedEvent: true, }, - EvGCMarkAssistActive: event.Spec{ + EvGCMarkAssistActive: { Name: "GCMarkAssistActive", Args: []string{"dt", "g"}, StartEv: EvGCMarkAssistBegin, IsTimedEvent: true, }, - EvGCMarkAssistBegin: event.Spec{ + EvGCMarkAssistBegin: { Name: "GCMarkAssistBegin", Args: []string{"dt", "stack"}, IsTimedEvent: true, StackIDs: []int{1}, }, - EvGCMarkAssistEnd: event.Spec{ + EvGCMarkAssistEnd: { Name: "GCMarkAssistEnd", Args: []string{"dt"}, StartEv: EvGCMarkAssistBegin, IsTimedEvent: true, }, - EvHeapAlloc: event.Spec{ + EvHeapAlloc: { Name: "HeapAlloc", Args: []string{"dt", "heapalloc_value"}, IsTimedEvent: true, }, - EvHeapGoal: event.Spec{ + EvHeapGoal: { Name: "HeapGoal", Args: []string{"dt", "heapgoal_value"}, IsTimedEvent: true, }, - EvGoLabel: event.Spec{ + EvGoLabel: { Name: "GoLabel", Args: []string{"dt", "label_string"}, IsTimedEvent: true, StringIDs: []int{1}, }, - EvUserTaskBegin: event.Spec{ + EvUserTaskBegin: { Name: "UserTaskBegin", Args: []string{"dt", "task", "parent_task", "name_string", "stack"}, IsTimedEvent: true, StackIDs: []int{4}, StringIDs: []int{3}, }, - EvUserTaskEnd: event.Spec{ + EvUserTaskEnd: { Name: "UserTaskEnd", Args: []string{"dt", "task", "stack"}, IsTimedEvent: true, StackIDs: []int{2}, }, - EvUserRegionBegin: event.Spec{ + EvUserRegionBegin: { Name: "UserRegionBegin", Args: []string{"dt", "task", "name_string", "stack"}, IsTimedEvent: true, StackIDs: []int{3}, StringIDs: []int{2}, }, - EvUserRegionEnd: event.Spec{ + EvUserRegionEnd: { Name: "UserRegionEnd", Args: []string{"dt", "task", "name_string", "stack"}, StartEv: EvUserRegionBegin, @@ -369,30 +390,30 @@ var specs = [...]event.Spec{ StackIDs: []int{3}, StringIDs: []int{2}, }, - EvUserLog: event.Spec{ + EvUserLog: { Name: "UserLog", Args: []string{"dt", "task", "key_string", "value_string", "stack"}, IsTimedEvent: true, StackIDs: []int{4}, StringIDs: []int{2, 3}, }, - EvGoSwitch: event.Spec{ + EvGoSwitch: { Name: "GoSwitch", Args: []string{"dt", "g", "g_seq"}, IsTimedEvent: true, }, - EvGoSwitchDestroy: event.Spec{ + EvGoSwitchDestroy: { Name: "GoSwitchDestroy", Args: []string{"dt", "g", "g_seq"}, IsTimedEvent: true, }, - EvGoCreateBlocked: event.Spec{ + EvGoCreateBlocked: { Name: "GoCreateBlocked", Args: []string{"dt", "new_g", "new_stack", "stack"}, IsTimedEvent: true, StackIDs: []int{3, 2}, }, - EvGoStatusStack: event.Spec{ + EvGoStatusStack: { Name: "GoStatusStack", Args: []string{"dt", "g", "m", "gstatus", "stack"}, IsTimedEvent: true, @@ -401,55 +422,55 @@ var specs = [...]event.Spec{ // Experimental events. - EvSpan: event.Spec{ + EvSpan: { Name: "Span", Args: []string{"dt", "id", "npages_value", "kindclass"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvSpanAlloc: event.Spec{ + EvSpanAlloc: { Name: "SpanAlloc", Args: []string{"dt", "id", "npages_value", "kindclass"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvSpanFree: event.Spec{ + EvSpanFree: { Name: "SpanFree", Args: []string{"dt", "id"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvHeapObject: event.Spec{ + EvHeapObject: { Name: "HeapObject", Args: []string{"dt", "id", "type"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvHeapObjectAlloc: event.Spec{ + EvHeapObjectAlloc: { Name: "HeapObjectAlloc", Args: []string{"dt", "id", "type"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvHeapObjectFree: event.Spec{ + EvHeapObjectFree: { Name: "HeapObjectFree", Args: []string{"dt", "id"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvGoroutineStack: event.Spec{ + EvGoroutineStack: { Name: "GoroutineStack", Args: []string{"dt", "id", "order"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvGoroutineStackAlloc: event.Spec{ + EvGoroutineStackAlloc: { Name: "GoroutineStackAlloc", Args: []string{"dt", "id", "order"}, IsTimedEvent: true, Experiment: AllocFree, }, - EvGoroutineStackFree: event.Spec{ + EvGoroutineStackFree: { Name: "GoroutineStackFree", Args: []string{"dt", "id"}, IsTimedEvent: true, @@ -457,6 +478,9 @@ var specs = [...]event.Spec{ }, } +// GoStatus is the status of a goroutine. +// +// They correspond directly to the various goroutine states. type GoStatus uint8 const ( @@ -481,6 +505,9 @@ func (s GoStatus) String() string { return "Bad" } +// ProcStatus is the status of a P. +// +// They mostly correspond to the various P states. type ProcStatus uint8 const ( @@ -488,6 +515,16 @@ const ( ProcRunning ProcIdle ProcSyscall + + // ProcSyscallAbandoned is a special case of + // ProcSyscall. It's used in the very specific case + // where the first a P is mentioned in a generation is + // part of a ProcSteal event. If that's the first time + // it's mentioned, then there's no GoSyscallBegin to + // connect the P stealing back to at that point. This + // special state indicates this to the parser, so it + // doesn't try to find a GoSyscallEndBlocked that + // corresponds with the ProcSteal. ProcSyscallAbandoned ) @@ -504,8 +541,30 @@ func (s ProcStatus) String() string { } const ( - // Various format-specific constants. - MaxBatchSize = 64 << 10 + // MaxBatchSize sets the maximum size that a batch can be. + // + // Directly controls the trace batch size in the runtime. + // + // NOTE: If this number decreases, the trace format version must change. + MaxBatchSize = 64 << 10 + + // Maximum number of PCs in a single stack trace. + // + // Since events contain only stack ID rather than whole stack trace, + // we can allow quite large values here. + // + // Directly controls the maximum number of frames per stack + // in the runtime. + // + // NOTE: If this number decreases, the trace format version must change. MaxFramesPerStack = 128 - MaxStringSize = 1 << 10 + + // MaxEventTrailerDataSize controls the amount of trailer data that + // an event can have in bytes. Must be smaller than MaxBatchSize. + // Controls the maximum string size in the trace. + // + // Directly controls the maximum such value in the runtime. + // + // NOTE: If this number decreases, the trace format version must change. + MaxEventTrailerDataSize = 1 << 10 ) diff --git a/src/internal/trace/tracev2/events_test.go b/src/internal/trace/tracev2/events_test.go new file mode 100644 index 00000000000000..60c4c08c34a127 --- /dev/null +++ b/src/internal/trace/tracev2/events_test.go @@ -0,0 +1,101 @@ +// Copyright 2023 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. + +package tracev2_test + +import ( + "internal/trace/tracev2" + "iter" + "regexp" + "slices" + "strings" + "testing" +) + +var argNameRegexp = regexp.MustCompile(`((?P[A-Za-z]+)_)?(?P[A-Za-z]+)`) + +func TestSpecs(t *testing.T) { + if tracev2.NumEvents <= 0 { + t.Fatalf("no trace events?") + } + if tracev2.MaxExperimentalEvent < tracev2.MaxEvent { + t.Fatalf("max experimental event (%d) is < max event (%d)", tracev2.MaxExperimentalEvent, tracev2.MaxEvent) + } + specs := tracev2.Specs() + for ev := range allEvents() { + spec := &specs[ev] + if spec.Name == "" { + t.Errorf("expected event %d to be defined in specs", ev) + continue + } + if spec.IsTimedEvent && spec.Args[0] != "dt" { + t.Errorf("%s is a timed event, but its first argument is not 'dt'", spec.Name) + } + if spec.HasData && spec.Name != "String" && spec.Name != "ExperimentalBatch" { + t.Errorf("%s has data, but is not a special kind of event (unsupported, but could be)", spec.Name) + } + if spec.IsStack && spec.Name != "Stack" { + t.Errorf("%s listed as being a stack, but is not the Stack event (unsupported)", spec.Name) + } + if spec.IsTimedEvent && len(spec.Args) > tracev2.MaxTimedEventArgs { + t.Errorf("%s has too many timed event args: have %d, want %d at most", spec.Name, len(spec.Args), tracev2.MaxTimedEventArgs) + } + if ev.Experimental() && spec.Experiment == tracev2.NoExperiment { + t.Errorf("experimental event %s must have an experiment", spec.Name) + } + + // Check arg types. + for _, arg := range spec.Args { + matches := argNameRegexp.FindStringSubmatch(arg) + if len(matches) == 0 { + t.Errorf("malformed argument %s for event %s", arg, spec.Name) + } + } + + // Check stacks. + for _, i := range spec.StackIDs { + if !strings.HasSuffix(spec.Args[i], "stack") { + t.Errorf("stack argument listed at %d in %s, but argument name %s does not imply stack type", i, spec.Name, spec.Args[i]) + } + } + for i, arg := range spec.Args { + if !strings.HasSuffix(spec.Args[i], "stack") { + continue + } + if !slices.Contains(spec.StackIDs, i) { + t.Errorf("found stack argument %s in %s at index %d not listed in StackIDs", arg, spec.Name, i) + } + } + + // Check strings. + for _, i := range spec.StringIDs { + if !strings.HasSuffix(spec.Args[i], "string") { + t.Errorf("string argument listed at %d in %s, but argument name %s does not imply string type", i, spec.Name, spec.Args[i]) + } + } + for i, arg := range spec.Args { + if !strings.HasSuffix(spec.Args[i], "string") { + continue + } + if !slices.Contains(spec.StringIDs, i) { + t.Errorf("found string argument %s in %s at index %d not listed in StringIDs", arg, spec.Name, i) + } + } + } +} + +func allEvents() iter.Seq[tracev2.EventType] { + return func(yield func(tracev2.EventType) bool) { + for ev := tracev2.EvNone + 1; ev < tracev2.NumEvents; ev++ { + if !yield(ev) { + return + } + } + for ev := tracev2.MaxEvent + 1; ev < tracev2.NumExperimentalEvents; ev++ { + if !yield(ev) { + return + } + } + } +} diff --git a/src/internal/trace/event/event.go b/src/internal/trace/tracev2/spec.go similarity index 76% rename from src/internal/trace/event/event.go rename to src/internal/trace/tracev2/spec.go index b8b6af00533231..6e54c399f49ddf 100644 --- a/src/internal/trace/event/event.go +++ b/src/internal/trace/tracev2/spec.go @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package event +package tracev2 -// Type indicates an event's type from which its arguments and semantics can be +// EventType indicates an event's type from which its arguments and semantics can be // derived. Its representation matches the wire format's representation of the event // types that precede all event data. -type Type uint8 +type EventType uint8 -// Spec is a specification for a trace event. It contains sufficient information +// EventSpec is a specification for a trace event. It contains sufficient information // to perform basic parsing of any trace event for any version of Go. -type Spec struct { +type EventSpec struct { // Name is the human-readable name of the trace event. Name string @@ -19,13 +19,16 @@ type Spec struct { // Its length determines the number of arguments an event has. // // Argument names follow a certain structure and this structure - // is relied on by the testing framework to type-check arguments. + // is relied on by the testing framework to type-check arguments + // and to produce Values for experimental events. + // // The structure is: // - // (?P[A-Za-z]+_)?(?P[A-Za-z]+) + // (?P[A-Za-z]+)(_(?P[A-Za-z]+))? // - // In sum, it's an optional name followed by a type. If the name - // is present, it is separated from the type with an underscore. + // In sum, it's a name followed by an optional type. + // If the type is present, it is preceded with an underscore. + // Arguments without types will be interpreted as just raw uint64s. // The valid argument types and the Go types they map to are listed // in the ArgTypes variable. Args []string @@ -42,7 +45,7 @@ type Spec struct { // StartEv indicates the event type of the corresponding "start" // event, if this event is an "end," for a pair of events that // represent a time range. - StartEv Type + StartEv EventType // IsTimedEvent indicates whether this is an event that both // appears in the main event stream and is surfaced to the @@ -72,10 +75,10 @@ type Spec struct { Experiment Experiment } -// ArgTypes is a list of valid argument types for use in Args. +// EventArgTypes is a list of valid argument types for use in Args. // // See the documentation of Args for more details. -var ArgTypes = [...]string{ +var EventArgTypes = [...]string{ "seq", // sequence number "pstatus", // P status "gstatus", // G status @@ -88,11 +91,11 @@ var ArgTypes = [...]string{ "task", // trace.TaskID } -// Names is a helper that produces a mapping of event names to event types. -func Names(specs []Spec) map[string]Type { - nameToType := make(map[string]Type) +// EventNames is a helper that produces a mapping of event names to event types. +func EventNames(specs []EventSpec) map[string]EventType { + nameToType := make(map[string]EventType) for i, spec := range specs { - nameToType[spec.Name] = Type(byte(i)) + nameToType[spec.Name] = EventType(byte(i)) } return nameToType } diff --git a/src/internal/trace/traceviewer/emitter.go b/src/internal/trace/traceviewer/emitter.go index c74f1c2ecf583d..9167ff81b45f83 100644 --- a/src/internal/trace/traceviewer/emitter.go +++ b/src/internal/trace/traceviewer/emitter.go @@ -632,10 +632,10 @@ func (e *Emitter) Flush() { e.processMeta(format.ProcsSection, e.resourceType, 2) - e.threadMeta(format.ProcsSection, trace.GCP, "GC", -6) - e.threadMeta(format.ProcsSection, trace.NetpollP, "Network", -5) - e.threadMeta(format.ProcsSection, trace.TimerP, "Timers", -4) - e.threadMeta(format.ProcsSection, trace.SyscallP, "Syscalls", -3) + e.threadMeta(format.ProcsSection, GCP, "GC", -6) + e.threadMeta(format.ProcsSection, NetpollP, "Network", -5) + e.threadMeta(format.ProcsSection, TimerP, "Timers", -4) + e.threadMeta(format.ProcsSection, SyscallP, "Syscalls", -3) for id, name := range e.resources { priority := int(id) @@ -683,12 +683,12 @@ func (e *Emitter) processMeta(sectionID uint64, name string, priority int) { // Stack emits the given frames and returns a unique id for the stack. No // pointers to the given data are being retained beyond the call to Stack. -func (e *Emitter) Stack(stk []*trace.Frame) int { +func (e *Emitter) Stack(stk []trace.StackFrame) int { return e.buildBranch(e.frameTree, stk) } // buildBranch builds one branch in the prefix tree rooted at ctx.frameTree. -func (e *Emitter) buildBranch(parent frameNode, stk []*trace.Frame) int { +func (e *Emitter) buildBranch(parent frameNode, stk []trace.StackFrame) int { if len(stk) == 0 { return parent.id } @@ -702,7 +702,7 @@ func (e *Emitter) buildBranch(parent frameNode, stk []*trace.Frame) int { node.id = e.frameSeq node.children = make(map[uint64]frameNode) parent.children[frame.PC] = node - e.c.ConsumeViewerFrame(strconv.Itoa(node.id), format.Frame{Name: fmt.Sprintf("%v:%v", frame.Fn, frame.Line), Parent: parent.id}) + e.c.ConsumeViewerFrame(strconv.Itoa(node.id), format.Frame{Name: fmt.Sprintf("%v:%v", frame.Func, frame.Line), Parent: parent.id}) } return e.buildBranch(node, stk) } diff --git a/src/internal/trace/traceviewer/fakep.go b/src/internal/trace/traceviewer/fakep.go new file mode 100644 index 00000000000000..655938b213bc36 --- /dev/null +++ b/src/internal/trace/traceviewer/fakep.go @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +package traceviewer + +const ( + // Special P identifiers: + FakeP = 1000000 + iota + TimerP // depicts timer unblocks + NetpollP // depicts network unblocks + SyscallP // depicts returns from syscalls + GCP // depicts GC state + ProfileP // depicts recording of CPU profile samples +) diff --git a/src/internal/trace/traceviewer/pprof.go b/src/internal/trace/traceviewer/pprof.go index 1377b3c614c81c..141b2687b781ac 100644 --- a/src/internal/trace/traceviewer/pprof.go +++ b/src/internal/trace/traceviewer/pprof.go @@ -82,7 +82,7 @@ func SVGProfileHandlerFunc(f ProfileFunc) http.HandlerFunc { } type ProfileRecord struct { - Stack []*trace.Frame + Stack []trace.StackFrame Count uint64 Time time.Duration } @@ -103,16 +103,16 @@ func BuildProfile(prof []ProfileRecord) *profile.Profile { for _, frame := range rec.Stack { loc := locs[frame.PC] if loc == nil { - fn := funcs[frame.File+frame.Fn] + fn := funcs[frame.File+frame.Func] if fn == nil { fn = &profile.Function{ ID: uint64(len(p.Function) + 1), - Name: frame.Fn, - SystemName: frame.Fn, + Name: frame.Func, + SystemName: frame.Func, Filename: frame.File, } p.Function = append(p.Function, fn) - funcs[frame.File+frame.Fn] = fn + funcs[frame.File+frame.Func] = fn } loc = &profile.Location{ ID: uint64(len(p.Location) + 1), diff --git a/src/internal/trace/value.go b/src/internal/trace/value.go index bd2cba7878355d..bf396b6a9ee394 100644 --- a/src/internal/trace/value.go +++ b/src/internal/trace/value.go @@ -4,12 +4,16 @@ package trace -import "fmt" +import ( + "fmt" + "unsafe" +) // Value is a dynamically-typed value obtained from a trace. type Value struct { - kind ValueKind - scalar uint64 + kind ValueKind + pointer unsafe.Pointer + scalar uint64 } // ValueKind is the type of a dynamically-typed value from a trace. @@ -18,6 +22,7 @@ type ValueKind uint8 const ( ValueBad ValueKind = iota ValueUint64 + ValueString ) // Kind returns the ValueKind of the value. @@ -30,24 +35,41 @@ func (v Value) Kind() ValueKind { return v.kind } -// Uint64 returns the uint64 value for a MetricSampleUint64. +// ToUint64 returns the uint64 value for a ValueUint64. // -// Panics if this metric sample's Kind is not MetricSampleUint64. -func (v Value) Uint64() uint64 { +// Panics if this Value's Kind is not ValueUint64. +func (v Value) ToUint64() uint64 { if v.kind != ValueUint64 { - panic("Uint64 called on Value of a different Kind") + panic("ToUint64 called on Value of a different Kind") } return v.scalar } -// valueAsString produces a debug string value. +// ToString returns the uint64 value for a ValueString. // -// This isn't just Value.String because we may want to use that to store -// string values in the future. -func valueAsString(v Value) string { +// Panics if this Value's Kind is not ValueString. +func (v Value) ToString() string { + if v.kind != ValueString { + panic("ToString called on Value of a different Kind") + } + return unsafe.String((*byte)(v.pointer), int(v.scalar)) +} + +func uint64Value(x uint64) Value { + return Value{kind: ValueUint64, scalar: x} +} + +func stringValue(s string) Value { + return Value{kind: ValueString, scalar: uint64(len(s)), pointer: unsafe.Pointer(unsafe.StringData(s))} +} + +// String returns the string representation of the value. +func (v Value) String() string { switch v.Kind() { case ValueUint64: - return fmt.Sprintf("Uint64(%d)", v.scalar) + return fmt.Sprintf("Value{Uint64(%d)}", v.ToUint64()) + case ValueString: + return fmt.Sprintf("Value{String(%s)}", v.ToString()) } - return "Bad" + return "Value{Bad}" } diff --git a/src/internal/trace/version/version.go b/src/internal/trace/version/version.go index 4951bd97d76645..8c460734cef268 100644 --- a/src/internal/trace/version/version.go +++ b/src/internal/trace/version/version.go @@ -8,41 +8,53 @@ import ( "fmt" "io" - "internal/trace/event" - "internal/trace/event/go122" + "internal/trace/tracev2" ) // Version represents the version of a trace file. type Version uint32 const ( - Go111 Version = 11 - Go119 Version = 19 - Go121 Version = 21 - Go122 Version = 22 - Go123 Version = 23 + Go111 Version = 11 // v1 + Go119 Version = 19 // v1 + Go121 Version = 21 // v1 + Go122 Version = 22 // v2 + Go123 Version = 23 // v2 Current = Go123 ) -var versions = map[Version][]event.Spec{ +var versions = map[Version][]tracev2.EventSpec{ // Go 1.11–1.21 use a different parser and are only set here for the sake of // Version.Valid. Go111: nil, Go119: nil, Go121: nil, - Go122: go122.Specs(), - // Go 1.23 adds backwards-incompatible events, but - // traces produced by Go 1.22 are also always valid - // Go 1.23 traces. - Go123: go122.Specs(), + Go122: tracev2.Specs()[:tracev2.EvUserLog+1], // All events after are Go 1.23+. + Go123: tracev2.Specs(), } // Specs returns the set of event.Specs for this version. -func (v Version) Specs() []event.Spec { +func (v Version) Specs() []tracev2.EventSpec { return versions[v] } +// EventName returns a string name of a wire format event +// for a particular trace version. +func (v Version) EventName(typ tracev2.EventType) string { + if !v.Valid() { + return "" + } + s := v.Specs() + if len(s) == 0 { + return "" + } + if int(typ) < len(s) && s[typ].Name != "" { + return s[typ].Name + } + return fmt.Sprintf("Invalid(%d)", typ) +} + func (v Version) Valid() bool { _, ok := versions[v] return ok diff --git a/src/internal/types/errors/code_string.go b/src/internal/types/errors/code_string.go index 9ae675ef849d87..26d7b48ee78a63 100644 --- a/src/internal/types/errors/code_string.go +++ b/src/internal/types/errors/code_string.go @@ -86,7 +86,6 @@ func _() { _ = x[MissingFieldOrMethod-76] _ = x[BadDotDotDotSyntax-77] _ = x[NonVariadicDotDotDot-78] - _ = x[MisplacedDotDotDot-79] _ = x[InvalidDotDotDot-81] _ = x[UncalledBuiltin-82] _ = x[InvalidAppend-83] @@ -161,7 +160,7 @@ func _() { const ( _Code_name_0 = "InvalidSyntaxTree" _Code_name_1 = "TestBlankPkgNameMismatchedPkgNameInvalidPkgUseBadImportPathBrokenImportImportCRenamedUnusedImportInvalidInitCycleDuplicateDeclInvalidDeclCycleInvalidTypeCycleInvalidConstInitInvalidConstValInvalidConstTypeUntypedNilUseWrongAssignCountUnassignableOperandNoNewVarMultiValAssignOpInvalidIfaceAssignInvalidChanAssignIncompatibleAssignUnaddressableFieldAssignNotATypeInvalidArrayLenBlankIfaceMethodIncomparableMapKey" - _Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDotMisplacedDotDotDot" + _Code_name_2 = "InvalidPtrEmbedBadRecvInvalidRecvDuplicateFieldAndMethodDuplicateMethodInvalidBlankInvalidIotaMissingInitBodyInvalidInitSigInvalidInitDeclInvalidMainDeclTooManyValuesNotAnExprTruncatedFloatNumericOverflowUndefinedOpMismatchedTypesDivByZeroNonNumericIncDecUnaddressableOperandInvalidIndirectionNonIndexableOperandInvalidIndexSwappedSliceIndicesNonSliceableOperandInvalidSliceExprInvalidShiftCountInvalidShiftOperandInvalidReceiveInvalidSendDuplicateLitKeyMissingLitKeyInvalidLitIndexOversizeArrayLitMixedStructLitInvalidStructLitMissingLitFieldDuplicateLitFieldUnexportedLitFieldInvalidLitFieldUntypedLitInvalidLitAmbiguousSelectorUndeclaredImportedNameUnexportedNameUndeclaredNameMissingFieldOrMethodBadDotDotDotSyntaxNonVariadicDotDotDot" _Code_name_3 = "InvalidDotDotDotUncalledBuiltinInvalidAppendInvalidCapInvalidCloseInvalidCopyInvalidComplexInvalidDeleteInvalidImagInvalidLenSwappedMakeArgsInvalidMakeInvalidRealInvalidAssertImpossibleAssertInvalidConversionInvalidUntypedConversionBadOffsetofSyntaxInvalidOffsetofUnusedExprUnusedVarMissingReturnWrongResultCountOutOfScopeResultInvalidCondInvalidPostDecl" _Code_name_4 = "InvalidIterVarInvalidRangeExprMisplacedBreakMisplacedContinueMisplacedFallthroughDuplicateCaseDuplicateDefaultBadTypeKeywordInvalidTypeSwitchInvalidExprSwitchInvalidSelectCaseUndeclaredLabelDuplicateLabelMisplacedLabelUnusedLabelJumpOverDeclJumpIntoBlockInvalidMethodExprWrongArgCountInvalidCallUnusedResultsInvalidDeferInvalidGoBadDeclRepeatedDeclInvalidUnsafeAddInvalidUnsafeSliceUnsupportedFeatureNotAGenericTypeWrongTypeArgCountCannotInferTypeArgsInvalidTypeArgInvalidInstanceCycleInvalidUnionMisplacedConstraintIfaceInvalidMethodTypeParamsMisplacedTypeParamInvalidUnsafeSliceDataInvalidUnsafeString" _Code_name_5 = "InvalidClearTypeTooLargeInvalidMinMaxOperandTooNew" @@ -169,7 +168,7 @@ const ( var ( _Code_index_1 = [...]uint16{0, 4, 16, 33, 46, 59, 71, 85, 97, 113, 126, 142, 158, 174, 189, 205, 218, 234, 253, 261, 277, 295, 312, 330, 354, 362, 377, 393, 411} - _Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738, 756} + _Code_index_2 = [...]uint16{0, 15, 22, 33, 56, 71, 83, 94, 109, 123, 138, 153, 166, 175, 189, 204, 215, 230, 239, 255, 275, 293, 312, 324, 343, 362, 378, 395, 414, 428, 439, 454, 467, 482, 498, 512, 528, 543, 560, 578, 593, 603, 613, 630, 652, 666, 680, 700, 718, 738} _Code_index_3 = [...]uint16{0, 16, 31, 44, 54, 66, 77, 91, 104, 115, 125, 140, 151, 162, 175, 191, 208, 232, 249, 264, 274, 283, 296, 312, 328, 339, 354} _Code_index_4 = [...]uint16{0, 14, 30, 44, 61, 81, 94, 110, 124, 141, 158, 175, 190, 204, 218, 229, 241, 254, 271, 284, 295, 308, 320, 329, 336, 348, 364, 382, 400, 415, 432, 451, 465, 485, 497, 521, 544, 562, 584, 603} _Code_index_5 = [...]uint8{0, 12, 24, 44, 50} @@ -182,7 +181,7 @@ func (i Code) String() string { case 1 <= i && i <= 28: i -= 1 return _Code_name_1[_Code_index_1[i]:_Code_index_1[i+1]] - case 30 <= i && i <= 79: + case 30 <= i && i <= 78: i -= 30 return _Code_name_2[_Code_index_2[i]:_Code_index_2[i+1]] case 81 <= i && i <= 106: diff --git a/src/internal/types/errors/codes.go b/src/internal/types/errors/codes.go index c0e6aa6c2daf52..f8c9eb920f5840 100644 --- a/src/internal/types/errors/codes.go +++ b/src/internal/types/errors/codes.go @@ -719,10 +719,7 @@ const ( // MisplacedDotDotDot occurs when a "..." is used somewhere other than the // final argument in a function declaration. - // - // Example: - // func f(...int, int) - MisplacedDotDotDot + _ // not used anymore (error reported by parser) _ // InvalidDotDotDotOperand was removed. diff --git a/src/internal/types/testdata/check/doubled_labels.go b/src/internal/types/testdata/check/doubled_labels.go new file mode 100644 index 00000000000000..f3de27020ba35b --- /dev/null +++ b/src/internal/types/testdata/check/doubled_labels.go @@ -0,0 +1,26 @@ +// Copyright 2014 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. + +package p + +func _() { +outer: +inner: + for { + continue inner + break inner + } + goto outer +} + +func _() { +outer: +inner: + for { + continue inner + continue outer /* ERROR "invalid continue label outer" */ + break outer /* ERROR "invalid break label outer" */ + } + goto outer +} diff --git a/src/internal/types/testdata/check/issue70974.go b/src/internal/types/testdata/check/issue70974.go new file mode 100644 index 00000000000000..59b11653cee18f --- /dev/null +++ b/src/internal/types/testdata/check/issue70974.go @@ -0,0 +1,27 @@ +// Copyright 2014 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. + +package p + +func _() { +outer: + for { + break outer + } + + for { + break outer /* ERROR "invalid break label outer" */ + } +} + +func _() { +outer: + for { + continue outer + } + + for { + continue outer /* ERROR "invalid continue label outer" */ + } +} diff --git a/src/internal/types/testdata/check/issues0.go b/src/internal/types/testdata/check/issues0.go index 44a709d66ea822..2b59a9c9b5c88a 100644 --- a/src/internal/types/testdata/check/issues0.go +++ b/src/internal/types/testdata/check/issues0.go @@ -326,9 +326,9 @@ func issue28281b(a, b int, c ...int) func issue28281c(a, b, c ... /* ERROR "can only use ... with final parameter" */ int) func issue28281d(... /* ERROR "can only use ... with final parameter" */ int, int) func issue28281e(a, b, c ... /* ERROR "can only use ... with final parameter" */ int, d int) -func issue28281f(... /* ERROR "can only use ... with final parameter" */ int, ... /* ERROR "can only use ... with final parameter" */ int, int) -func (... /* ERROR "invalid use of '...'" */ TT) f() -func issue28281g() (... /* ERROR "can only use ... with final parameter" */ TT) +func issue28281f(... /* ERROR "can only use ... with final parameter" */ int, ... int, int) +func (... /* ERROR "invalid use of ..." */ TT) f() +func issue28281g() (... /* ERROR "invalid use of ..." */ TT) // Issue #26234: Make various field/method lookup errors easier to read by matching cmd/compile's output func issue26234a(f *syn.Prog) { diff --git a/src/internal/types/testdata/examples/types.go b/src/internal/types/testdata/examples/types.go index 67f1534be39479..d6da2c5f6f9f9f 100644 --- a/src/internal/types/testdata/examples/types.go +++ b/src/internal/types/testdata/examples/types.go @@ -114,7 +114,7 @@ type I1[T any] interface{ } // There is no such thing as a variadic generic type. -type _[T ... /* ERROR "invalid use of '...'" */ any] struct{} +type _[T ... /* ERROR "invalid use of ..." */ any] struct{} // Generic interfaces may be embedded as one would expect. type I2 interface { diff --git a/src/internal/types/testdata/fixedbugs/issue43671.go b/src/internal/types/testdata/fixedbugs/issue43671.go index be4c9ee5dd1f51..19da7e0ccca207 100644 --- a/src/internal/types/testdata/fixedbugs/issue43671.go +++ b/src/internal/types/testdata/fixedbugs/issue43671.go @@ -12,11 +12,11 @@ type C4 interface{ chan int | chan<- int } type C5[T any] interface{ ~chan T | <-chan T } func _[T any](ch T) { - <-ch // ERRORx `cannot receive from ch .* \(no core type\)` + <-ch // ERRORx `cannot receive from ch .*: type set contains no specific channel type` } func _[T C0](ch T) { - <-ch // ERROR "cannot receive from non-channel ch" + <-ch // ERRORx `cannot receive from ch .*: type set contains non-channel int` } func _[T C1](ch T) { @@ -28,11 +28,11 @@ func _[T C2](ch T) { } func _[T C3](ch T) { - <-ch // ERRORx `cannot receive from ch .* \(no core type\)` + <-ch // ERRORx `cannot receive from ch .*: type set contains channels with different element types int and float32` } func _[T C4](ch T) { - <-ch // ERROR "cannot receive from send-only channel" + <-ch // ERRORx `cannot receive from ch .*: type set contains send-only channel chan<- int` } func _[T C5[X], X any](ch T, x X) { diff --git a/src/internal/types/testdata/fixedbugs/issue47115.go b/src/internal/types/testdata/fixedbugs/issue47115.go index 2d2be34104216e..1de85b3791a2d0 100644 --- a/src/internal/types/testdata/fixedbugs/issue47115.go +++ b/src/internal/types/testdata/fixedbugs/issue47115.go @@ -12,11 +12,11 @@ type C4 interface{ chan int | chan<- int } type C5[T any] interface{ ~chan T | chan<- T } func _[T any](ch T) { - ch <- /* ERRORx `cannot send to ch .* no core type` */ 0 + ch <- /* ERRORx `cannot send to ch .*: type set contains no specific channel type` */ 0 } func _[T C0](ch T) { - ch <- /* ERROR "cannot send to non-channel" */ 0 + ch <- /* ERRORx `cannot send to ch .*: type set contains non-channel int` */ 0 } func _[T C1](ch T) { @@ -24,11 +24,11 @@ func _[T C1](ch T) { } func _[T C2](ch T) { - ch <-/* ERROR "cannot send to receive-only channel" */ 0 + ch <- /* ERRORx `cannot send to ch .*: type set contains receive-only channel <-chan int` */ 0 } func _[T C3](ch T) { - ch <- /* ERRORx `cannot send to ch .* no core type` */ 0 + ch <- /* ERRORx `cannot send to ch .*: type set contains channels with different element types` */ 0 } func _[T C4](ch T) { diff --git a/src/internal/types/testdata/fixedbugs/issue71131.go b/src/internal/types/testdata/fixedbugs/issue71131.go new file mode 100644 index 00000000000000..8e7575b02828de --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue71131.go @@ -0,0 +1,15 @@ +// Copyright 2025 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. + +package p + +func _() { + type Bool bool + for range func /* ERROR "yield func returns user-defined boolean, not bool" */ (func() Bool) {} { + } + for range func /* ERROR "yield func returns user-defined boolean, not bool" */ (func(int) Bool) {} { + } + for range func /* ERROR "yield func returns user-defined boolean, not bool" */ (func(int, string) Bool) {} { + } +} diff --git a/src/internal/types/testdata/fixedbugs/issue71198.go b/src/internal/types/testdata/fixedbugs/issue71198.go new file mode 100644 index 00000000000000..479f8e2b0c4e64 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue71198.go @@ -0,0 +1,16 @@ +// -gotypesalias=1 + +// Copyright 2025 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. + +package p + +type A[_ any] = any + +// This must not panic; also the error message must match the style for non-alias types, below. +func _[_ A /* ERROR "too many type arguments for type A: have 2, want 1" */ [int, string]]() {} + +type T[_ any] any + +func _[_ T /* ERROR "too many type arguments for type T: have 2, want 1" */ [int, string]]() {} diff --git a/src/internal/types/testdata/fixedbugs/issue71284.go b/src/internal/types/testdata/fixedbugs/issue71284.go new file mode 100644 index 00000000000000..4b73087a78a191 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue71284.go @@ -0,0 +1,10 @@ +// Copyright 2025 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. + +package A + +type ( + _ = A + A /* ERROR "invalid recursive type: A refers to itself" */ = A +) diff --git a/src/internal/types/testdata/spec/range.go b/src/internal/types/testdata/spec/range.go index 52d1e70382bd2e..c0f579479f60ec 100644 --- a/src/internal/types/testdata/spec/range.go +++ b/src/internal/types/testdata/spec/range.go @@ -5,7 +5,7 @@ package p type MyInt int32 -type MyBool bool +type MyBool = bool // TODO(gri) remove alias declaration - see go.dev/issues/71131, go.dev/issues/71164 type MyString string type MyFunc1 func(func(int) bool) type MyFunc2 func(int) bool diff --git a/src/io/fs/readlink.go b/src/io/fs/readlink.go new file mode 100644 index 00000000000000..64340b9fb4c57b --- /dev/null +++ b/src/io/fs/readlink.go @@ -0,0 +1,45 @@ +// Copyright 2023 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. + +package fs + +// ReadLinkFS is the interface implemented by a file system +// that supports reading symbolic links. +type ReadLinkFS interface { + FS + + // ReadLink returns the destination of the named symbolic link. + // If there is an error, it should be of type [*PathError]. + ReadLink(name string) (string, error) + + // Lstat returns a [FileInfo] describing the named file. + // If the file is a symbolic link, the returned [FileInfo] describes the symbolic link. + // Lstat makes no attempt to follow the link. + // If there is an error, it should be of type [*PathError]. + Lstat(name string) (FileInfo, error) +} + +// ReadLink returns the destination of the named symbolic link. +// +// If fsys does not implement [ReadLinkFS], then ReadLink returns an error. +func ReadLink(fsys FS, name string) (string, error) { + sym, ok := fsys.(ReadLinkFS) + if !ok { + return "", &PathError{Op: "readlink", Path: name, Err: ErrInvalid} + } + return sym.ReadLink(name) +} + +// Lstat returns a [FileInfo] describing the named file. +// If the file is a symbolic link, the returned [FileInfo] describes the symbolic link. +// Lstat makes no attempt to follow the link. +// +// If fsys does not implement [ReadLinkFS], then Lstat is identical to [Stat]. +func Lstat(fsys FS, name string) (FileInfo, error) { + sym, ok := fsys.(ReadLinkFS) + if !ok { + return Stat(fsys, name) + } + return sym.Lstat(name) +} diff --git a/src/io/fs/readlink_test.go b/src/io/fs/readlink_test.go new file mode 100644 index 00000000000000..3932c7b7785daf --- /dev/null +++ b/src/io/fs/readlink_test.go @@ -0,0 +1,106 @@ +// Copyright 2023 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. + +package fs_test + +import ( + . "io/fs" + "testing" + "testing/fstest" +) + +func TestReadLink(t *testing.T) { + testFS := fstest.MapFS{ + "foo": { + Data: []byte("bar"), + Mode: ModeSymlink | 0o777, + }, + "bar": { + Data: []byte("Hello, World!\n"), + Mode: 0o644, + }, + + "dir/parentlink": { + Data: []byte("../bar"), + Mode: ModeSymlink | 0o777, + }, + "dir/link": { + Data: []byte("file"), + Mode: ModeSymlink | 0o777, + }, + "dir/file": { + Data: []byte("Hello, World!\n"), + Mode: 0o644, + }, + } + + check := func(fsys FS, name string, want string) { + t.Helper() + got, err := ReadLink(fsys, name) + if got != want || err != nil { + t.Errorf("ReadLink(%q) = %q, %v; want %q, ", name, got, err, want) + } + } + + check(testFS, "foo", "bar") + check(testFS, "dir/parentlink", "../bar") + check(testFS, "dir/link", "file") + + // Test that ReadLink on Sub works. + sub, err := Sub(testFS, "dir") + if err != nil { + t.Fatal(err) + } + + check(sub, "link", "file") + check(sub, "parentlink", "../bar") +} + +func TestLstat(t *testing.T) { + testFS := fstest.MapFS{ + "foo": { + Data: []byte("bar"), + Mode: ModeSymlink | 0o777, + }, + "bar": { + Data: []byte("Hello, World!\n"), + Mode: 0o644, + }, + + "dir/parentlink": { + Data: []byte("../bar"), + Mode: ModeSymlink | 0o777, + }, + "dir/link": { + Data: []byte("file"), + Mode: ModeSymlink | 0o777, + }, + "dir/file": { + Data: []byte("Hello, World!\n"), + Mode: 0o644, + }, + } + + check := func(fsys FS, name string, want FileMode) { + t.Helper() + info, err := Lstat(fsys, name) + var got FileMode + if err == nil { + got = info.Mode() + } + if got != want || err != nil { + t.Errorf("Lstat(%q) = %v, %v; want %v, ", name, got, err, want) + } + } + + check(testFS, "foo", ModeSymlink|0o777) + check(testFS, "bar", 0o644) + + // Test that Lstat on Sub works. + sub, err := Sub(testFS, "dir") + if err != nil { + t.Fatal(err) + } + check(sub, "link", ModeSymlink|0o777) +} diff --git a/src/io/fs/sub.go b/src/io/fs/sub.go index 70ac62307778ed..376d561bad8447 100644 --- a/src/io/fs/sub.go +++ b/src/io/fs/sub.go @@ -23,7 +23,8 @@ type SubFS interface { // Otherwise, if fs implements [SubFS], Sub returns fsys.Sub(dir). // Otherwise, Sub returns a new [FS] implementation sub that, // in effect, implements sub.Open(name) as fsys.Open(path.Join(dir, name)). -// The implementation also translates calls to ReadDir, ReadFile, and Glob appropriately. +// The implementation also translates calls to ReadDir, ReadFile, +// ReadLink, Lstat, and Glob appropriately. // // Note that Sub(os.DirFS("/"), "prefix") is equivalent to os.DirFS("/prefix") // and that neither of them guarantees to avoid operating system @@ -44,6 +45,12 @@ func Sub(fsys FS, dir string) (FS, error) { return &subFS{fsys, dir}, nil } +var _ FS = (*subFS)(nil) +var _ ReadDirFS = (*subFS)(nil) +var _ ReadFileFS = (*subFS)(nil) +var _ ReadLinkFS = (*subFS)(nil) +var _ GlobFS = (*subFS)(nil) + type subFS struct { fsys FS dir string @@ -105,6 +112,30 @@ func (f *subFS) ReadFile(name string) ([]byte, error) { return data, f.fixErr(err) } +func (f *subFS) ReadLink(name string) (string, error) { + full, err := f.fullName("readlink", name) + if err != nil { + return "", err + } + target, err := ReadLink(f.fsys, full) + if err != nil { + return "", f.fixErr(err) + } + return target, nil +} + +func (f *subFS) Lstat(name string) (FileInfo, error) { + full, err := f.fullName("lstat", name) + if err != nil { + return nil, err + } + info, err := Lstat(f.fsys, full) + if err != nil { + return nil, f.fixErr(err) + } + return info, nil +} + func (f *subFS) Glob(pattern string) ([]string, error) { // Check pattern is well-formed. if _, err := path.Match(pattern, ""); err != nil { diff --git a/src/io/fs/walk_test.go b/src/io/fs/walk_test.go index a5fc715e155dbb..91f195f7be3df2 100644 --- a/src/io/fs/walk_test.go +++ b/src/io/fs/walk_test.go @@ -110,6 +110,47 @@ func TestWalkDir(t *testing.T) { }) } +func TestWalkDirSymlink(t *testing.T) { + fsys := fstest.MapFS{ + "link": {Data: []byte("dir"), Mode: ModeSymlink}, + "dir/a": {}, + "dir/b/c": {}, + "dir/d": {Data: []byte("b"), Mode: ModeSymlink}, + } + + wantTypes := map[string]FileMode{ + "link": ModeDir, + "link/a": 0, + "link/b": ModeDir, + "link/b/c": 0, + "link/d": ModeSymlink, + } + marks := make(map[string]int) + walkFn := func(path string, entry DirEntry, err error) error { + marks[path]++ + if want, ok := wantTypes[path]; !ok { + t.Errorf("Unexpected path %q in walk", path) + } else if got := entry.Type(); got != want { + t.Errorf("%s entry type = %v; want %v", path, got, want) + } + if err != nil { + t.Errorf("Walking %s: %v", path, err) + } + return nil + } + + // Expect no errors. + err := WalkDir(fsys, "link", walkFn) + if err != nil { + t.Fatalf("no error expected, found: %s", err) + } + for path := range wantTypes { + if got := marks[path]; got != 1 { + t.Errorf("%s visited %d times; expected 1", path, got) + } + } +} + func TestIssue51617(t *testing.T) { dir := t.TempDir() for _, sub := range []string{"a", filepath.Join("a", "bad"), filepath.Join("a", "next")} { diff --git a/src/io/ioutil/ioutil.go b/src/io/ioutil/ioutil.go index af8ebe38501d4c..0ab4b5a0c333d9 100644 --- a/src/io/ioutil/ioutil.go +++ b/src/io/ioutil/ioutil.go @@ -24,6 +24,8 @@ import ( // as an error to be reported. // // Deprecated: As of Go 1.16, this function simply calls [io.ReadAll]. +// +//go:fix inline func ReadAll(r io.Reader) ([]byte, error) { return io.ReadAll(r) } @@ -34,6 +36,8 @@ func ReadAll(r io.Reader) ([]byte, error) { // to be reported. // // Deprecated: As of Go 1.16, this function simply calls [os.ReadFile]. +// +//go:fix inline func ReadFile(filename string) ([]byte, error) { return os.ReadFile(filename) } @@ -43,6 +47,8 @@ func ReadFile(filename string) ([]byte, error) { // (before umask); otherwise WriteFile truncates it before writing, without changing permissions. // // Deprecated: As of Go 1.16, this function simply calls [os.WriteFile]. +// +//go:fix inline func WriteFile(filename string, data []byte, perm fs.FileMode) error { return os.WriteFile(filename, data, perm) } @@ -87,6 +93,8 @@ func ReadDir(dirname string) ([]fs.FileInfo, error) { // the provided Reader r. // // Deprecated: As of Go 1.16, this function simply calls [io.NopCloser]. +// +//go:fix inline func NopCloser(r io.Reader) io.ReadCloser { return io.NopCloser(r) } diff --git a/src/io/ioutil/tempfile.go b/src/io/ioutil/tempfile.go index 47b2e4012f7622..ef2ce404d9ae8f 100644 --- a/src/io/ioutil/tempfile.go +++ b/src/io/ioutil/tempfile.go @@ -21,6 +21,8 @@ import ( // to remove the file when no longer needed. // // Deprecated: As of Go 1.17, this function simply calls [os.CreateTemp]. +// +//go:fix inline func TempFile(dir, pattern string) (f *os.File, err error) { return os.CreateTemp(dir, pattern) } @@ -36,6 +38,8 @@ func TempFile(dir, pattern string) (f *os.File, err error) { // to remove the directory when no longer needed. // // Deprecated: As of Go 1.17, this function simply calls [os.MkdirTemp]. +// +//go:fix inline func TempDir(dir, pattern string) (name string, err error) { return os.MkdirTemp(dir, pattern) } diff --git a/src/io/multi_test.go b/src/io/multi_test.go index 7a24a8afc5a419..934a6ec785fda4 100644 --- a/src/io/multi_test.go +++ b/src/io/multi_test.go @@ -332,9 +332,7 @@ func TestMultiReaderFreesExhaustedReaders(t *testing.T) { buf1 := bytes.NewReader([]byte("foo")) buf2 := bytes.NewReader([]byte("bar")) mr = MultiReader(buf1, buf2) - runtime.SetFinalizer(buf1, func(*bytes.Reader) { - close(closed) - }) + runtime.AddCleanup(buf1, func(ch chan struct{}) { close(ch) }, closed) }() buf := make([]byte, 4) diff --git a/src/iter/iter.go b/src/iter/iter.go index 14fd8f8115f3f6..e765378ef25f45 100644 --- a/src/iter/iter.go +++ b/src/iter/iter.go @@ -28,7 +28,22 @@ or index-value pairs. Yield returns true if the iterator should continue with the next element in the sequence, false if it should stop. -Iterator functions are most often called by a range loop, as in: +For instance, [maps.Keys] returns an iterator that produces the sequence +of keys of the map m, implemented as follows: + + func Keys[Map ~map[K]V, K comparable, V any](m Map) iter.Seq[K] { + return func(yield func(K) bool) { + for k := range m { + if !yield(k) { + return + } + } + } + } + +Further examples can be found in [The Go Blog: Range Over Function Types]. + +Iterator functions are most often called by a [range loop], as in: func PrintAll[V any](seq iter.Seq[V]) { for v := range seq { @@ -187,6 +202,9 @@ And then a client could delete boring values from the tree using: p.Delete() } } + +[The Go Blog: Range Over Function Types]: https://go.dev/blog/range-functions +[range loop]: https://go.dev/ref/spec#For_range */ package iter diff --git a/src/log/slog/example_discard_test.go b/src/log/slog/example_discard_test.go index c0cc2a65aaf8c7..3e3e37b1890908 100644 --- a/src/log/slog/example_discard_test.go +++ b/src/log/slog/example_discard_test.go @@ -10,7 +10,7 @@ import ( "os" ) -func ExampleDiscardHandler() { +func Example_discardHandler() { // A slog.TextHandler can output log messages. logger1 := slog.New(slog.NewTextHandler( os.Stdout, diff --git a/src/make.bat b/src/make.bat index 3b5a4663dce342..6c683230ce1714 100644 --- a/src/make.bat +++ b/src/make.bat @@ -34,25 +34,12 @@ @echo off -:: Keep environment variables within this script -:: unless invoked with --no-local. -if x%1==x-no-local goto nolocal -if x%2==x-no-local goto nolocal -if x%3==x-no-local goto nolocal -if x%4==x-no-local goto nolocal -if x%1==x--no-local goto nolocal -if x%2==x--no-local goto nolocal -if x%3==x--no-local goto nolocal -if x%4==x--no-local goto nolocal setlocal -:nolocal -set GOBUILDFAIL=0 - -if exist make.bat goto ok -echo Must run make.bat from Go src directory. -goto fail -:ok +if not exist make.bat ( + echo Must run make.bat from Go src directory. + exit /b 1 +) :: Clean old generated file that will cause problems in the build. del /F ".\pkg\runtime\runtime_defs.go" 2>NUL @@ -91,7 +78,11 @@ if "x%GOROOT_BOOTSTRAP%"=="x" if exist "%HOMEDRIVE%%HOMEPATH%\sdk\go%bootgo%" se if "x%GOROOT_BOOTSTRAP%"=="x" set GOROOT_BOOTSTRAP=%HOMEDRIVE%%HOMEPATH%\Go1.4 :bootstrapset -if not exist "%GOROOT_BOOTSTRAP%\bin\go.exe" goto bootstrapfail +if not exist "%GOROOT_BOOTSTRAP%\bin\go.exe" ( + echo ERROR: Cannot find %GOROOT_BOOTSTRAP%\bin\go.exe + echo Set GOROOT_BOOTSTRAP to a working Go tree ^>= Go %bootgo%. + exit /b 1 +) set GOROOT=%GOROOT_TEMP% set GOROOT_TEMP= @@ -103,55 +94,26 @@ echo Building Go cmd/dist using %GOROOT_BOOTSTRAP%. (%GOROOT_BOOTSTRAP_VERSION%) if x%vflag==x-v echo cmd/dist set GOROOT=%GOROOT_BOOTSTRAP% set GOBIN= -"%GOROOT_BOOTSTRAP%\bin\go.exe" build -o cmd\dist\dist.exe .\cmd\dist +"%GOROOT_BOOTSTRAP%\bin\go.exe" build -o cmd\dist\dist.exe .\cmd\dist || exit /b 1 endlocal -if errorlevel 1 goto fail -.\cmd\dist\dist.exe env -w -p >env.bat -if errorlevel 1 goto fail +.\cmd\dist\dist.exe env -w -p >env.bat || exit /b 1 call .\env.bat del env.bat if x%vflag==x-v echo. -if x%1==x-dist-tool goto copydist -if x%2==x-dist-tool goto copydist -if x%3==x-dist-tool goto copydist -if x%4==x-dist-tool goto copydist -if x%1==x--dist-tool goto copydist -if x%2==x--dist-tool goto copydist -if x%3==x--dist-tool goto copydist -if x%4==x--dist-tool goto copydist - -set bootstrapflags= -if x%1==x-no-clean set bootstrapflags=-no-clean -if x%2==x-no-clean set bootstrapflags=-no-clean -if x%3==x-no-clean set bootstrapflags=-no-clean -if x%4==x-no-clean set bootstrapflags=-no-clean -if x%1==x--no-clean set bootstrapflags=-no-clean -if x%2==x--no-clean set bootstrapflags=-no-clean -if x%3==x--no-clean set bootstrapflags=-no-clean -if x%4==x--no-clean set bootstrapflags=-no-clean -if x%1==x-no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%2==x-no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%3==x-no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%4==x-no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%1==x--no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%2==x--no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%3==x--no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%4==x--no-banner set bootstrapflags=%bootstrapflags% -no-banner -if x%1==x-distpack set bootstrapflags=%bootstrapflags% -distpack -if x%2==x-distpack set bootstrapflags=%bootstrapflags% -distpack -if x%3==x-distpack set bootstrapflags=%bootstrapflags% -distpack -if x%4==x-distpack set bootstrapflags=%bootstrapflags% -distpack -if x%1==x--distpack set bootstrapflags=%bootstrapflags% -distpack -if x%2==x--distpack set bootstrapflags=%bootstrapflags% -distpack -if x%3==x--distpack set bootstrapflags=%bootstrapflags% -distpack -if x%4==x--distpack set bootstrapflags=%bootstrapflags% -distpack +if x%1==x--dist-tool ( + mkdir "%GOTOOLDIR%" 2>NUL + if not x%2==x ( + copy cmd\dist\dist.exe "%2" + ) + move cmd\dist\dist.exe "%GOTOOLDIR%\dist.exe" + goto :eof +) :: Run dist bootstrap to complete make.bash. :: Bootstrap installs a proper cmd/dist, built with the new toolchain. :: Throw ours, built with the bootstrap toolchain, away after bootstrap. -.\cmd\dist\dist.exe bootstrap -a %vflag% %bootstrapflags% -if errorlevel 1 goto fail +.\cmd\dist\dist.exe bootstrap -a %* || exit /b 1 del .\cmd\dist\dist.exe goto :eof @@ -161,11 +123,6 @@ goto :eof :: to avoid needing three copies in three different shell languages :: (make.bash, make.bat, make.rc). -:copydist -mkdir "%GOTOOLDIR%" 2>NUL -copy cmd\dist\dist.exe "%GOTOOLDIR%\" -goto :eof - :nogoenv set GO111MODULE=off set GOENV=off @@ -173,12 +130,3 @@ set GOOS= set GOARCH= set GOEXPERIMENT= set GOFLAGS= -goto :eof - -:bootstrapfail -echo ERROR: Cannot find %GOROOT_BOOTSTRAP%\bin\go.exe -echo Set GOROOT_BOOTSTRAP to a working Go tree ^>= Go %bootgo%. - -:fail -set GOBUILDFAIL=1 -if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL% diff --git a/src/math/big/float.go b/src/math/big/float.go index 813c4ebfa7477f..e1d20d8bb4c008 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -602,7 +602,7 @@ func (z *Float) SetInt(x *Int) *Float { // are many trailing 0's. bits := uint32(x.BitLen()) if z.prec == 0 { - z.prec = umax32(bits, 64) + z.prec = max(bits, 64) } z.acc = Exact z.neg = x.neg @@ -628,7 +628,7 @@ func (z *Float) SetRat(x *Rat) *Float { a.SetInt(x.Num()) b.SetInt(x.Denom()) if z.prec == 0 { - z.prec = umax32(a.prec, b.prec) + z.prec = max(a.prec, b.prec) } return z.Quo(&a, &b) } @@ -1451,7 +1451,7 @@ func (z *Float) Add(x, y *Float) *Float { } if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) + z.prec = max(x.prec, y.prec) } if x.form == finite && y.form == finite { @@ -1525,7 +1525,7 @@ func (z *Float) Sub(x, y *Float) *Float { } if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) + z.prec = max(x.prec, y.prec) } if x.form == finite && y.form == finite { @@ -1592,7 +1592,7 @@ func (z *Float) Mul(x, y *Float) *Float { } if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) + z.prec = max(x.prec, y.prec) } z.neg = x.neg != y.neg @@ -1637,7 +1637,7 @@ func (z *Float) Quo(x, y *Float) *Float { } if z.prec == 0 { - z.prec = umax32(x.prec, y.prec) + z.prec = max(x.prec, y.prec) } z.neg = x.neg != y.neg @@ -1724,10 +1724,3 @@ func (x *Float) ord() int { } return m } - -func umax32(x, y uint32) uint32 { - if x > y { - return x - } - return y -} diff --git a/src/net/example_test.go b/src/net/example_test.go index 2c045d73a24bcb..12c83970940ab0 100644 --- a/src/net/example_test.go +++ b/src/net/example_test.go @@ -334,7 +334,7 @@ func ExampleIP_To16() { // 10.255.0.0 } -func ExampleIP_to4() { +func ExampleIP_To4() { ipv6 := net.IP{0xfc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} ipv4 := net.IPv4(10, 255, 0, 0) diff --git a/src/net/http/client.go b/src/net/http/client.go index fda7815436c25c..9231f63e65b134 100644 --- a/src/net/http/client.go +++ b/src/net/http/client.go @@ -610,8 +610,9 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { reqBodyClosed = false // have we closed the current req.Body? // Redirect behavior: - redirectMethod string - includeBody = true + redirectMethod string + includeBody = true + stripSensitiveHeaders = false ) uerr := func(err error) error { // the body may have been closed already by c.send() @@ -678,7 +679,12 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { // in case the user set Referer on their first request. // If they really want to override, they can do it in // their CheckRedirect func. - copyHeaders(req) + if !stripSensitiveHeaders && reqs[0].URL.Host != req.URL.Host { + if !shouldCopyHeaderOnRedirect(reqs[0].URL, req.URL) { + stripSensitiveHeaders = true + } + } + copyHeaders(req, stripSensitiveHeaders) // Add the Referer header from the most recent // request URL to the new one, if it's not https->http: @@ -746,7 +752,7 @@ func (c *Client) do(req *Request) (retres *Response, reterr error) { // makeHeadersCopier makes a function that copies headers from the // initial Request, ireq. For every redirect, this function must be called // so that it can copy headers into the upcoming Request. -func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) { +func (c *Client) makeHeadersCopier(ireq *Request) func(req *Request, stripSensitiveHeaders bool) { // The headers to copy are from the very initial request. // We use a closured callback to keep a reference to these original headers. var ( @@ -760,8 +766,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) { } } - preq := ireq // The previous request - return func(req *Request) { + return func(req *Request, stripSensitiveHeaders bool) { // If Jar is present and there was some initial cookies provided // via the request header, then we may need to alter the initial // cookies as we follow redirects since each redirect may end up @@ -798,12 +803,15 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) { // Copy the initial request's Header values // (at least the safe ones). for k, vv := range ireqhdr { - if shouldCopyHeaderOnRedirect(k, preq.URL, req.URL) { + sensitive := false + switch CanonicalHeaderKey(k) { + case "Authorization", "Www-Authenticate", "Cookie", "Cookie2": + sensitive = true + } + if !(sensitive && stripSensitiveHeaders) { req.Header[k] = vv } } - - preq = req // Update previous Request with the current request } } @@ -979,28 +987,23 @@ func (b *cancelTimerBody) Close() error { return err } -func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool { - switch CanonicalHeaderKey(headerKey) { - case "Authorization", "Www-Authenticate", "Cookie", "Cookie2": - // Permit sending auth/cookie headers from "foo.com" - // to "sub.foo.com". - - // Note that we don't send all cookies to subdomains - // automatically. This function is only used for - // Cookies set explicitly on the initial outgoing - // client request. Cookies automatically added via the - // CookieJar mechanism continue to follow each - // cookie's scope as set by Set-Cookie. But for - // outgoing requests with the Cookie header set - // directly, we don't know their scope, so we assume - // it's for *.domain.com. - - ihost := idnaASCIIFromURL(initial) - dhost := idnaASCIIFromURL(dest) - return isDomainOrSubdomain(dhost, ihost) - } - // All other headers are copied: - return true +func shouldCopyHeaderOnRedirect(initial, dest *url.URL) bool { + // Permit sending auth/cookie headers from "foo.com" + // to "sub.foo.com". + + // Note that we don't send all cookies to subdomains + // automatically. This function is only used for + // Cookies set explicitly on the initial outgoing + // client request. Cookies automatically added via the + // CookieJar mechanism continue to follow each + // cookie's scope as set by Set-Cookie. But for + // outgoing requests with the Cookie header set + // directly, we don't know their scope, so we assume + // it's for *.domain.com. + + ihost := idnaASCIIFromURL(initial) + dhost := idnaASCIIFromURL(dest) + return isDomainOrSubdomain(dhost, ihost) } // isDomainOrSubdomain reports whether sub is a subdomain (or exact diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index 429b8f1d2cd556..1ce9539528c668 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -1536,6 +1536,55 @@ func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) { } } +// Issue #70530: Once we strip a header on a redirect to a different host, +// the header should stay stripped across any further redirects. +func TestClientStripHeadersOnRepeatedRedirect(t *testing.T) { + run(t, testClientStripHeadersOnRepeatedRedirect) +} +func testClientStripHeadersOnRepeatedRedirect(t *testing.T, mode testMode) { + var proto string + ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) { + if r.Host+r.URL.Path != "a.example.com/" { + if h := r.Header.Get("Authorization"); h != "" { + t.Errorf("on request to %v%v, Authorization=%q, want no header", r.Host, r.URL.Path, h) + } + } + // Follow a chain of redirects from a to b and back to a. + // The Authorization header is stripped on the first redirect to b, + // and stays stripped even if we're sent back to a. + switch r.Host + r.URL.Path { + case "a.example.com/": + Redirect(w, r, proto+"://b.example.com/", StatusFound) + case "b.example.com/": + Redirect(w, r, proto+"://b.example.com/redirect", StatusFound) + case "b.example.com/redirect": + Redirect(w, r, proto+"://a.example.com/redirect", StatusFound) + case "a.example.com/redirect": + w.Header().Set("X-Done", "true") + default: + t.Errorf("unexpected request to %v", r.URL) + } + })).ts + proto, _, _ = strings.Cut(ts.URL, ":") + + c := ts.Client() + c.Transport.(*Transport).Dial = func(_ string, _ string) (net.Conn, error) { + return net.Dial("tcp", ts.Listener.Addr().String()) + } + + req, _ := NewRequest("GET", proto+"://a.example.com/", nil) + req.Header.Add("Cookie", "foo=bar") + req.Header.Add("Authorization", "secretpassword") + res, err := c.Do(req) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + if res.Header.Get("X-Done") != "true" { + t.Fatalf("response missing expected header: X-Done=true") + } +} + // Issue 22233: copy host when Client follows a relative redirect. func TestClientCopyHostOnRedirect(t *testing.T) { run(t, testClientCopyHostOnRedirect) } func testClientCopyHostOnRedirect(t *testing.T, mode testMode) { @@ -1702,43 +1751,39 @@ func testClientAltersCookiesOnRedirect(t *testing.T, mode testMode) { // Part of Issue 4800 func TestShouldCopyHeaderOnRedirect(t *testing.T) { tests := []struct { - header string initialURL string destURL string want bool }{ - {"User-Agent", "http://foo.com/", "http://bar.com/", true}, - {"X-Foo", "http://foo.com/", "http://bar.com/", true}, - // Sensitive headers: - {"cookie", "http://foo.com/", "http://bar.com/", false}, - {"cookie2", "http://foo.com/", "http://bar.com/", false}, - {"authorization", "http://foo.com/", "http://bar.com/", false}, - {"authorization", "http://foo.com/", "https://foo.com/", true}, - {"authorization", "http://foo.com:1234/", "http://foo.com:4321/", true}, - {"www-authenticate", "http://foo.com/", "http://bar.com/", false}, - {"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false}, + {"http://foo.com/", "http://bar.com/", false}, + {"http://foo.com/", "http://bar.com/", false}, + {"http://foo.com/", "http://bar.com/", false}, + {"http://foo.com/", "https://foo.com/", true}, + {"http://foo.com:1234/", "http://foo.com:4321/", true}, + {"http://foo.com/", "http://bar.com/", false}, + {"http://foo.com/", "http://[::1%25.foo.com]/", false}, // But subdomains should work: - {"www-authenticate", "http://foo.com/", "http://foo.com/", true}, - {"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true}, - {"www-authenticate", "http://foo.com/", "http://notfoo.com/", false}, - {"www-authenticate", "http://foo.com/", "https://foo.com/", true}, - {"www-authenticate", "http://foo.com:80/", "http://foo.com/", true}, - {"www-authenticate", "http://foo.com:80/", "http://sub.foo.com/", true}, - {"www-authenticate", "http://foo.com:443/", "https://foo.com/", true}, - {"www-authenticate", "http://foo.com:443/", "https://sub.foo.com/", true}, - {"www-authenticate", "http://foo.com:1234/", "http://foo.com/", true}, - - {"authorization", "http://foo.com/", "http://foo.com/", true}, - {"authorization", "http://foo.com/", "http://sub.foo.com/", true}, - {"authorization", "http://foo.com/", "http://notfoo.com/", false}, - {"authorization", "http://foo.com/", "https://foo.com/", true}, - {"authorization", "http://foo.com:80/", "http://foo.com/", true}, - {"authorization", "http://foo.com:80/", "http://sub.foo.com/", true}, - {"authorization", "http://foo.com:443/", "https://foo.com/", true}, - {"authorization", "http://foo.com:443/", "https://sub.foo.com/", true}, - {"authorization", "http://foo.com:1234/", "http://foo.com/", true}, + {"http://foo.com/", "http://foo.com/", true}, + {"http://foo.com/", "http://sub.foo.com/", true}, + {"http://foo.com/", "http://notfoo.com/", false}, + {"http://foo.com/", "https://foo.com/", true}, + {"http://foo.com:80/", "http://foo.com/", true}, + {"http://foo.com:80/", "http://sub.foo.com/", true}, + {"http://foo.com:443/", "https://foo.com/", true}, + {"http://foo.com:443/", "https://sub.foo.com/", true}, + {"http://foo.com:1234/", "http://foo.com/", true}, + + {"http://foo.com/", "http://foo.com/", true}, + {"http://foo.com/", "http://sub.foo.com/", true}, + {"http://foo.com/", "http://notfoo.com/", false}, + {"http://foo.com/", "https://foo.com/", true}, + {"http://foo.com:80/", "http://foo.com/", true}, + {"http://foo.com:80/", "http://sub.foo.com/", true}, + {"http://foo.com:443/", "https://foo.com/", true}, + {"http://foo.com:443/", "https://sub.foo.com/", true}, + {"http://foo.com:1234/", "http://foo.com/", true}, } for i, tt := range tests { u0, err := url.Parse(tt.initialURL) @@ -1751,10 +1796,10 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) { t.Errorf("%d. dest URL %q parse error: %v", i, tt.destURL, err) continue } - got := Export_shouldCopyHeaderOnRedirect(tt.header, u0, u1) + got := Export_shouldCopyHeaderOnRedirect(u0, u1) if got != tt.want { - t.Errorf("%d. shouldCopyHeaderOnRedirect(%q, %q => %q) = %v; want %v", - i, tt.header, tt.initialURL, tt.destURL, got, tt.want) + t.Errorf("%d. shouldCopyHeaderOnRedirect(%q => %q) = %v; want %v", + i, tt.initialURL, tt.destURL, got, tt.want) } } } diff --git a/src/net/http/clientserver_test.go b/src/net/http/clientserver_test.go index 32d97ea9f083ce..208c6509fac758 100644 --- a/src/net/http/clientserver_test.go +++ b/src/net/http/clientserver_test.go @@ -1253,7 +1253,7 @@ func testTransportGCRequest(t *testing.T, mode testMode, body bool) { (func() { body := strings.NewReader("some body") req, _ := NewRequest("POST", cst.ts.URL, body) - runtime.SetFinalizer(req, func(*Request) { close(didGC) }) + runtime.AddCleanup(req, func(ch chan struct{}) { close(ch) }, didGC) res, err := cst.c.Do(req) if err != nil { t.Fatal(err) diff --git a/src/net/http/export_test.go b/src/net/http/export_test.go index 56ebda180bb085..f2aa663a990d5f 100644 --- a/src/net/http/export_test.go +++ b/src/net/http/export_test.go @@ -21,7 +21,6 @@ import ( var ( DefaultUserAgent = defaultUserAgent NewLoggingConn = newLoggingConn - ExportAppendTime = appendTime ExportRefererForURL = refererForURL ExportServerNewConn = (*Server).newConn ExportCloseWriteAndWait = (*conn).closeWriteAndWait diff --git a/src/net/http/fs.go b/src/net/http/fs.go index 3a716fbd2cc7f8..48ba05a664dbaf 100644 --- a/src/net/http/fs.go +++ b/src/net/http/fs.go @@ -854,7 +854,7 @@ func containsDotDot(v string) bool { if !strings.Contains(v, "..") { return false } - for _, ent := range strings.FieldsFunc(v, isSlashRune) { + for ent := range strings.FieldsFuncSeq(v, isSlashRune) { if ent == ".." { return true } @@ -1014,7 +1014,7 @@ func parseRange(s string, size int64) ([]httpRange, error) { } var ranges []httpRange noOverlap := false - for _, ra := range strings.Split(s[len(b):], ",") { + for ra := range strings.SplitSeq(s[len(b):], ",") { ra = textproto.TrimString(ra) if ra == "" { continue diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index 46a2b79231e565..9933c413ac84d4 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -1,7 +1,7 @@ //go:build !nethttpomithttp2 // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. -// $ bundle -o=h2_bundle.go -prefix=http2 -tags=!nethttpomithttp2 golang.org/x/net/http2 +// $ bundle -o=h2_bundle.go -prefix=http2 -tags=!nethttpomithttp2 -import=golang.org/x/net/internal/httpcommon=net/http/internal/httpcommon golang.org/x/net/http2 // Package http2 implements the HTTP/2 protocol. // @@ -40,6 +40,7 @@ import ( mathrand "math/rand" "net" "net/http/httptrace" + "net/http/internal/httpcommon" "net/textproto" "net/url" "os" @@ -1085,7 +1086,7 @@ func http2configFromServer(h1 *Server, h2 *http2Server) http2http2Config { return conf } -// configFromServer merges configuration settings from h2 and h2.t1.HTTP2 +// configFromTransport merges configuration settings from h2 and h2.t1.HTTP2 // (the net/http Transport). func http2configFromTransport(h2 *http2Transport) http2http2Config { conf := http2http2Config{ @@ -1151,7 +1152,7 @@ func http2fillNetHTTPServerConfig(conf *http2http2Config, srv *Server) { http2fillNetHTTPConfig(conf, srv.HTTP2) } -// fillNetHTTPServerConfig sets fields in conf from tr.HTTP2. +// fillNetHTTPTransportConfig sets fields in conf from tr.HTTP2. func http2fillNetHTTPTransportConfig(conf *http2http2Config, tr *Transport) { http2fillNetHTTPConfig(conf, tr.HTTP2) } @@ -3414,106 +3415,19 @@ func http2cutoff64(base int) uint64 { } var ( - http2commonBuildOnce sync.Once - http2commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case - http2commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case -) - -func http2buildCommonHeaderMapsOnce() { - http2commonBuildOnce.Do(http2buildCommonHeaderMaps) -} - -func http2buildCommonHeaderMaps() { - common := []string{ - "accept", - "accept-charset", - "accept-encoding", - "accept-language", - "accept-ranges", - "age", - "access-control-allow-credentials", - "access-control-allow-headers", - "access-control-allow-methods", - "access-control-allow-origin", - "access-control-expose-headers", - "access-control-max-age", - "access-control-request-headers", - "access-control-request-method", - "allow", - "authorization", - "cache-control", - "content-disposition", - "content-encoding", - "content-language", - "content-length", - "content-location", - "content-range", - "content-type", - "cookie", - "date", - "etag", - "expect", - "expires", - "from", - "host", - "if-match", - "if-modified-since", - "if-none-match", - "if-unmodified-since", - "last-modified", - "link", - "location", - "max-forwards", - "origin", - "proxy-authenticate", - "proxy-authorization", - "range", - "referer", - "refresh", - "retry-after", - "server", - "set-cookie", - "strict-transport-security", - "trailer", - "transfer-encoding", - "user-agent", - "vary", - "via", - "www-authenticate", - "x-forwarded-for", - "x-forwarded-proto", - } - http2commonLowerHeader = make(map[string]string, len(common)) - http2commonCanonHeader = make(map[string]string, len(common)) - for _, v := range common { - chk := CanonicalHeaderKey(v) - http2commonLowerHeader[chk] = v - http2commonCanonHeader[v] = chk - } -} - -func http2lowerHeader(v string) (lower string, ascii bool) { - http2buildCommonHeaderMapsOnce() - if s, ok := http2commonLowerHeader[v]; ok { - return s, true - } - return http2asciiToLower(v) -} - -func http2canonicalHeader(v string) string { - http2buildCommonHeaderMapsOnce() - if s, ok := http2commonCanonHeader[v]; ok { - return s - } - return CanonicalHeaderKey(v) -} - -var ( - http2VerboseLogs bool - http2logFrameWrites bool - http2logFrameReads bool - http2inTests bool - http2disableExtendedConnectProtocol bool + http2VerboseLogs bool + http2logFrameWrites bool + http2logFrameReads bool + http2inTests bool + + // Enabling extended CONNECT by causes browsers to attempt to use + // WebSockets-over-HTTP/2. This results in problems when the server's websocket + // package doesn't support extended CONNECT. + // + // Disable extended CONNECT by default for now. + // + // Issue #71128. + http2disableExtendedConnectProtocol = true ) func init() { @@ -3526,8 +3440,8 @@ func init() { http2logFrameWrites = true http2logFrameReads = true } - if strings.Contains(e, "http2xconnect=0") { - http2disableExtendedConnectProtocol = true + if strings.Contains(e, "http2xconnect=1") { + http2disableExtendedConnectProtocol = false } } @@ -3886,23 +3800,6 @@ func (s *http2sorter) SortStrings(ss []string) { s.v = save } -// validPseudoPath reports whether v is a valid :path pseudo-header -// value. It must be either: -// -// - a non-empty string starting with '/' -// - the string '*', for OPTIONS requests. -// -// For now this is only used a quick check for deciding when to clean -// up Opaque URLs before sending requests from the Transport. -// See golang.org/issue/16847 -// -// We used to enforce that the path also didn't start with "//", but -// Google's GFE accepts such paths and Chrome sends them, so ignore -// that part of the spec. See golang.org/issue/19103. -func http2validPseudoPath(v string) bool { - return (len(v) > 0 && v[0] == '/') || v == "*" -} - // incomparable is a zero-width, non-comparable type. Adding it to a struct // makes that struct also non-comparable, and generally doesn't add // any size (as long as it's first). @@ -4855,8 +4752,7 @@ const http2maxCachedCanonicalHeadersKeysSize = 2048 func (sc *http2serverConn) canonicalHeader(v string) string { sc.serveG.check() - http2buildCommonHeaderMapsOnce() - cv, ok := http2commonCanonHeader[v] + cv, ok := httpcommon.CachedCanonicalHeader(v) if ok { return cv } @@ -7789,6 +7685,7 @@ type http2ClientConn struct { doNotReuse bool // whether conn is marked to not be reused for any future requests closing bool closed bool + closedOnIdle bool // true if conn was closed for idleness seenSettings bool // true if we've seen a settings frame, false otherwise seenSettingsChan chan struct{} // closed when seenSettings is true or frame reading fails wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back @@ -8504,10 +8401,12 @@ func (cc *http2ClientConn) idleStateLocked() (st http2clientConnIdleState) { // If this connection has never been used for a request and is closed, // then let it take a request (which will fail). + // If the conn was closed for idleness, we're racing the idle timer; + // don't try to use the conn. (Issue #70515.) // // This avoids a situation where an error early in a connection's lifetime // goes unreported. - if cc.nextStreamID == 1 && cc.streamsReserved == 0 && cc.closed { + if cc.nextStreamID == 1 && cc.streamsReserved == 0 && cc.closed && !cc.closedOnIdle { st.canTakeNewRequest = true } @@ -8570,6 +8469,7 @@ func (cc *http2ClientConn) closeIfIdle() { return } cc.closed = true + cc.closedOnIdle = true nextID := cc.nextStreamID // TODO: do clients send GOAWAY too? maybe? Just Close: cc.mu.Unlock() @@ -8686,23 +8586,6 @@ func (cc *http2ClientConn) closeForLostPing() { // exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests. var http2errRequestCanceled = errors.New("net/http: request canceled") -func http2commaSeparatedTrailers(req *Request) (string, error) { - keys := make([]string, 0, len(req.Trailer)) - for k := range req.Trailer { - k = http2canonicalHeader(k) - switch k { - case "Transfer-Encoding", "Trailer", "Content-Length": - return "", fmt.Errorf("invalid Trailer key %q", k) - } - keys = append(keys, k) - } - if len(keys) > 0 { - sort.Strings(keys) - return strings.Join(keys, ","), nil - } - return "", nil -} - func (cc *http2ClientConn) responseHeaderTimeout() time.Duration { if cc.t.t1 != nil { return cc.t.t1.ResponseHeaderTimeout @@ -8714,22 +8597,6 @@ func (cc *http2ClientConn) responseHeaderTimeout() time.Duration { return 0 } -// checkConnHeaders checks whether req has any invalid connection-level headers. -// per RFC 7540 section 8.1.2.2: Connection-Specific Header Fields. -// Certain headers are special-cased as okay but not transmitted later. -func http2checkConnHeaders(req *Request) error { - if v := req.Header.Get("Upgrade"); v != "" { - return fmt.Errorf("http2: invalid Upgrade request header: %q", req.Header["Upgrade"]) - } - if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") { - return fmt.Errorf("http2: invalid Transfer-Encoding request header: %q", vv) - } - if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !http2asciiEqualFold(vv[0], "close") && !http2asciiEqualFold(vv[0], "keep-alive")) { - return fmt.Errorf("http2: invalid Connection request header: %q", vv) - } - return nil -} - // actualContentLength returns a sanitized version of // req.ContentLength, where 0 actually means zero (not unknown) and -1 // means unknown. @@ -8775,25 +8642,7 @@ func (cc *http2ClientConn) roundTrip(req *Request, streamf func(*http2clientStre donec: make(chan struct{}), } - // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? - if !cc.t.disableCompression() && - req.Header.Get("Accept-Encoding") == "" && - req.Header.Get("Range") == "" && - !cs.isHead { - // Request gzip only, not deflate. Deflate is ambiguous and - // not as universally supported anyway. - // See: https://zlib.net/zlib_faq.html#faq39 - // - // Note that we don't request this for HEAD requests, - // due to a bug in nginx: - // http://trac.nginx.org/nginx/ticket/358 - // https://golang.org/issue/5522 - // - // We don't request gzip if the request is for a range, since - // auto-decoding a portion of a gzipped document will just fail - // anyway. See https://golang.org/issue/8923 - cs.requestedGzip = true - } + cs.requestedGzip = httpcommon.IsRequestGzip(req.Method, req.Header, cc.t.disableCompression()) go cs.doRequest(req, streamf) @@ -8907,10 +8756,6 @@ func (cs *http2clientStream) writeRequest(req *Request, streamf func(*http2clien cc := cs.cc ctx := cs.ctx - if err := http2checkConnHeaders(req); err != nil { - return err - } - // wait for setting frames to be received, a server can change this value later, // but we just wait for the first settings frame var isExtendedConnect bool @@ -9074,26 +8919,39 @@ func (cs *http2clientStream) encodeAndWriteHeaders(req *Request) error { // we send: HEADERS{1}, CONTINUATION{0,} + DATA{0,} (DATA is // sent by writeRequestBody below, along with any Trailers, // again in form HEADERS{1}, CONTINUATION{0,}) - trailers, err := http2commaSeparatedTrailers(req) - if err != nil { - return err - } - hasTrailers := trailers != "" - contentLen := http2actualContentLength(req) - hasBody := contentLen != 0 - hdrs, err := cc.encodeHeaders(req, cs.requestedGzip, trailers, contentLen) + cc.hbuf.Reset() + res, err := http2encodeRequestHeaders(req, cs.requestedGzip, cc.peerMaxHeaderListSize, func(name, value string) { + cc.writeHeader(name, value) + }) if err != nil { - return err + return fmt.Errorf("http2: %w", err) } + hdrs := cc.hbuf.Bytes() // Write the request. - endStream := !hasBody && !hasTrailers + endStream := !res.HasBody && !res.HasTrailers cs.sentHeaders = true err = cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs) http2traceWroteHeaders(cs.trace) return err } +func http2encodeRequestHeaders(req *Request, addGzipHeader bool, peerMaxHeaderListSize uint64, headerf func(name, value string)) (httpcommon.EncodeHeadersResult, error) { + return httpcommon.EncodeHeaders(req.Context(), httpcommon.EncodeHeadersParam{ + Request: httpcommon.Request{ + Header: req.Header, + Trailer: req.Trailer, + URL: req.URL, + Host: req.Host, + Method: req.Method, + ActualContentLength: http2actualContentLength(req), + }, + AddGzipHeader: addGzipHeader, + PeerMaxHeaderListSize: peerMaxHeaderListSize, + DefaultUserAgent: http2defaultUserAgent, + }, headerf) +} + // cleanupWriteRequest performs post-request tasks. // // If err (the result of writeRequest) is non-nil and the stream is not closed, @@ -9482,218 +9340,6 @@ func (cs *http2clientStream) awaitFlowControl(maxBytes int) (taken int32, err er } } -func http2validateHeaders(hdrs Header) string { - for k, vv := range hdrs { - if !httpguts.ValidHeaderFieldName(k) && k != ":protocol" { - return fmt.Sprintf("name %q", k) - } - for _, v := range vv { - if !httpguts.ValidHeaderFieldValue(v) { - // Don't include the value in the error, - // because it may be sensitive. - return fmt.Sprintf("value for header %q", k) - } - } - } - return "" -} - -var http2errNilRequestURL = errors.New("http2: Request.URI is nil") - -func http2isNormalConnect(req *Request) bool { - return req.Method == "CONNECT" && req.Header.Get(":protocol") == "" -} - -// requires cc.wmu be held. -func (cc *http2ClientConn) encodeHeaders(req *Request, addGzipHeader bool, trailers string, contentLength int64) ([]byte, error) { - cc.hbuf.Reset() - if req.URL == nil { - return nil, http2errNilRequestURL - } - - host := req.Host - if host == "" { - host = req.URL.Host - } - host, err := httpguts.PunycodeHostPort(host) - if err != nil { - return nil, err - } - if !httpguts.ValidHostHeader(host) { - return nil, errors.New("http2: invalid Host header") - } - - var path string - if !http2isNormalConnect(req) { - path = req.URL.RequestURI() - if !http2validPseudoPath(path) { - orig := path - path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host) - if !http2validPseudoPath(path) { - if req.URL.Opaque != "" { - return nil, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque) - } else { - return nil, fmt.Errorf("invalid request :path %q", orig) - } - } - } - } - - // Check for any invalid headers+trailers and return an error before we - // potentially pollute our hpack state. (We want to be able to - // continue to reuse the hpack encoder for future requests) - if err := http2validateHeaders(req.Header); err != "" { - return nil, fmt.Errorf("invalid HTTP header %s", err) - } - if err := http2validateHeaders(req.Trailer); err != "" { - return nil, fmt.Errorf("invalid HTTP trailer %s", err) - } - - enumerateHeaders := func(f func(name, value string)) { - // 8.1.2.3 Request Pseudo-Header Fields - // The :path pseudo-header field includes the path and query parts of the - // target URI (the path-absolute production and optionally a '?' character - // followed by the query production, see Sections 3.3 and 3.4 of - // [RFC3986]). - f(":authority", host) - m := req.Method - if m == "" { - m = MethodGet - } - f(":method", m) - if !http2isNormalConnect(req) { - f(":path", path) - f(":scheme", req.URL.Scheme) - } - if trailers != "" { - f("trailer", trailers) - } - - var didUA bool - for k, vv := range req.Header { - if http2asciiEqualFold(k, "host") || http2asciiEqualFold(k, "content-length") { - // Host is :authority, already sent. - // Content-Length is automatic, set below. - continue - } else if http2asciiEqualFold(k, "connection") || - http2asciiEqualFold(k, "proxy-connection") || - http2asciiEqualFold(k, "transfer-encoding") || - http2asciiEqualFold(k, "upgrade") || - http2asciiEqualFold(k, "keep-alive") { - // Per 8.1.2.2 Connection-Specific Header - // Fields, don't send connection-specific - // fields. We have already checked if any - // are error-worthy so just ignore the rest. - continue - } else if http2asciiEqualFold(k, "user-agent") { - // Match Go's http1 behavior: at most one - // User-Agent. If set to nil or empty string, - // then omit it. Otherwise if not mentioned, - // include the default (below). - didUA = true - if len(vv) < 1 { - continue - } - vv = vv[:1] - if vv[0] == "" { - continue - } - } else if http2asciiEqualFold(k, "cookie") { - // Per 8.1.2.5 To allow for better compression efficiency, the - // Cookie header field MAY be split into separate header fields, - // each with one or more cookie-pairs. - for _, v := range vv { - for { - p := strings.IndexByte(v, ';') - if p < 0 { - break - } - f("cookie", v[:p]) - p++ - // strip space after semicolon if any. - for p+1 <= len(v) && v[p] == ' ' { - p++ - } - v = v[p:] - } - if len(v) > 0 { - f("cookie", v) - } - } - continue - } - - for _, v := range vv { - f(k, v) - } - } - if http2shouldSendReqContentLength(req.Method, contentLength) { - f("content-length", strconv.FormatInt(contentLength, 10)) - } - if addGzipHeader { - f("accept-encoding", "gzip") - } - if !didUA { - f("user-agent", http2defaultUserAgent) - } - } - - // Do a first pass over the headers counting bytes to ensure - // we don't exceed cc.peerMaxHeaderListSize. This is done as a - // separate pass before encoding the headers to prevent - // modifying the hpack state. - hlSize := uint64(0) - enumerateHeaders(func(name, value string) { - hf := hpack.HeaderField{Name: name, Value: value} - hlSize += uint64(hf.Size()) - }) - - if hlSize > cc.peerMaxHeaderListSize { - return nil, http2errRequestHeaderListSize - } - - trace := httptrace.ContextClientTrace(req.Context()) - traceHeaders := http2traceHasWroteHeaderField(trace) - - // Header list size is ok. Write the headers. - enumerateHeaders(func(name, value string) { - name, ascii := http2lowerHeader(name) - if !ascii { - // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header - // field names have to be ASCII characters (just as in HTTP/1.x). - return - } - cc.writeHeader(name, value) - if traceHeaders { - http2traceWroteHeaderField(trace, name, value) - } - }) - - return cc.hbuf.Bytes(), nil -} - -// shouldSendReqContentLength reports whether the http2.Transport should send -// a "content-length" request header. This logic is basically a copy of the net/http -// transferWriter.shouldSendContentLength. -// The contentLength is the corrected contentLength (so 0 means actually 0, not unknown). -// -1 means unknown. -func http2shouldSendReqContentLength(method string, contentLength int64) bool { - if contentLength > 0 { - return true - } - if contentLength < 0 { - return false - } - // For zero bodies, whether we send a content-length depends on the method. - // It also kinda doesn't matter for http2 either way, with END_STREAM. - switch method { - case "POST", "PUT", "PATCH": - return true - default: - return false - } -} - // requires cc.wmu be held. func (cc *http2ClientConn) encodeTrailers(trailer Header) ([]byte, error) { cc.hbuf.Reset() @@ -9710,7 +9356,7 @@ func (cc *http2ClientConn) encodeTrailers(trailer Header) ([]byte, error) { } for k, vv := range trailer { - lowKey, ascii := http2lowerHeader(k) + lowKey, ascii := httpcommon.LowerHeader(k) if !ascii { // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header // field names have to be ASCII characters (just as in HTTP/1.x). @@ -9850,9 +9496,12 @@ func (rl *http2clientConnReadLoop) cleanup() { // This avoids a situation where new connections are constantly created, // added to the pool, fail, and are removed from the pool, without any error // being surfaced to the user. - const unusedWaitTime = 5 * time.Second + unusedWaitTime := 5 * time.Second + if cc.idleTimeout > 0 && unusedWaitTime > cc.idleTimeout { + unusedWaitTime = cc.idleTimeout + } idleTime := cc.t.now().Sub(cc.lastActive) - if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime { + if atomic.LoadUint32(&cc.atomicReused) == 0 && idleTime < unusedWaitTime && !cc.closedOnIdle { cc.idleTimer = cc.t.afterFunc(unusedWaitTime-idleTime, func() { cc.t.connPool().MarkDead(cc) }) @@ -10062,7 +9711,7 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http Status: status + " " + StatusText(statusCode), } for _, hf := range regularFields { - key := http2canonicalHeader(hf.Name) + key := httpcommon.CanonicalHeader(hf.Name) if key == "Trailer" { t := res.Trailer if t == nil { @@ -10070,7 +9719,7 @@ func (rl *http2clientConnReadLoop) handleResponse(cs *http2clientStream, f *http res.Trailer = t } http2foreachHeaderElement(hf.Value, func(v string) { - t[http2canonicalHeader(v)] = nil + t[httpcommon.CanonicalHeader(v)] = nil }) } else { vv := header[key] @@ -10194,7 +9843,7 @@ func (rl *http2clientConnReadLoop) processTrailers(cs *http2clientStream, f *htt trailer := make(Header) for _, hf := range f.RegularFields() { - key := http2canonicalHeader(hf.Name) + key := httpcommon.CanonicalHeader(hf.Name) trailer[key] = append(trailer[key], hf.Value) } cs.trailer = trailer @@ -10740,7 +10389,7 @@ func (cc *http2ClientConn) writeStreamReset(streamID uint32, code http2ErrCode, var ( http2errResponseHeaderListSize = errors.New("http2: response header list larger than advertised limit") - http2errRequestHeaderListSize = errors.New("http2: request header list larger than peer's advertised limit") + http2errRequestHeaderListSize = httpcommon.ErrRequestHeaderListSize ) func (cc *http2ClientConn) logf(format string, args ...interface{}) { @@ -10927,16 +10576,6 @@ func http2traceFirstResponseByte(trace *httptrace.ClientTrace) { } } -func http2traceHasWroteHeaderField(trace *httptrace.ClientTrace) bool { - return trace != nil && trace.WroteHeaderField != nil -} - -func http2traceWroteHeaderField(trace *httptrace.ClientTrace, k, v string) { - if trace != nil && trace.WroteHeaderField != nil { - trace.WroteHeaderField(k, []string{v}) - } -} - func http2traceGot1xxResponseFunc(trace *httptrace.ClientTrace) func(int, textproto.MIMEHeader) error { if trace != nil { return trace.Got1xxResponse @@ -11319,7 +10958,7 @@ func http2encodeHeaders(enc *hpack.Encoder, h Header, keys []string) { } for _, k := range keys { vv := h[k] - k, ascii := http2lowerHeader(k) + k, ascii := httpcommon.LowerHeader(k) if !ascii { // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header // field names have to be ASCII characters (just as in HTTP/1.x). diff --git a/src/net/http/http.go b/src/net/http/http.go index 4da77889b1a341..e1e9eea0cefe93 100644 --- a/src/net/http/http.go +++ b/src/net/http/http.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:generate bundle -o=h2_bundle.go -prefix=http2 -tags=!nethttpomithttp2 golang.org/x/net/http2 +//go:generate bundle -o=h2_bundle.go -prefix=http2 -tags=!nethttpomithttp2 -import=golang.org/x/net/internal/httpcommon=net/http/internal/httpcommon golang.org/x/net/http2 package http @@ -17,6 +17,7 @@ import ( ) // Protocols is a set of HTTP protocols. +// The zero value is an empty set of protocols. // // The supported protocols are: // diff --git a/src/net/http/http_test.go b/src/net/http/http_test.go index 5aba3ed5a6e536..c12bbedac986db 100644 --- a/src/net/http/http_test.go +++ b/src/net/http/http_test.go @@ -164,7 +164,9 @@ func TestNoUnicodeStrings(t *testing.T) { } if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") || - path == "h2_bundle.go" || d.IsDir() { + path == "h2_bundle.go" || + path == "internal/httpcommon/httpcommon.go" || + d.IsDir() { return nil } diff --git a/src/net/http/httptest/httptest.go b/src/net/http/httptest/httptest.go index 0c0dbb40e89bc5..7fe7107a9a191c 100644 --- a/src/net/http/httptest/httptest.go +++ b/src/net/http/httptest/httptest.go @@ -34,9 +34,9 @@ func NewRequest(method, target string, body io.Reader) *http.Request { // // An empty method means "GET". // -// The provided body may be nil. If the body is of type *bytes.Reader, -// *strings.Reader, or *bytes.Buffer, the Request.ContentLength is -// set. +// The provided body may be nil. If the body is of type [bytes.Reader], +// [strings.Reader], [bytes.Buffer], or the value [http.NoBody], +// the Request.ContentLength is set. // // NewRequest panics on error for ease of use in testing, where a // panic is acceptable. @@ -69,6 +69,9 @@ func NewRequestWithContext(ctx context.Context, method, target string, body io.R default: req.ContentLength = -1 } + if body == http.NoBody { + req.ContentLength = 0 + } if rc, ok := body.(io.ReadCloser); ok { req.Body = rc } else { diff --git a/src/net/http/httptest/httptest_test.go b/src/net/http/httptest/httptest_test.go index d5a4c3dc9d19a1..5f2215cfc6cdc1 100644 --- a/src/net/http/httptest/httptest_test.go +++ b/src/net/http/httptest/httptest_test.go @@ -156,6 +156,24 @@ func TestNewRequestWithContext(t *testing.T) { wantBody: "foo", }, + { + name: "Post with NoBody", + method: "POST", + uri: "/", + body: http.NoBody, + want: &http.Request{ + Method: "POST", + Host: "example.com", + URL: &url.URL{Path: "/"}, + Header: http.Header{}, + Proto: "HTTP/1.1", + ProtoMajor: 1, + ProtoMinor: 1, + RemoteAddr: "192.0.2.1:1234", + RequestURI: "/", + }, + }, + { name: "OPTIONS *", method: "OPTIONS", diff --git a/src/net/http/httptest/recorder.go b/src/net/http/httptest/recorder.go index dd51901b0d3b94..17aa70f06760a2 100644 --- a/src/net/http/httptest/recorder.go +++ b/src/net/http/httptest/recorder.go @@ -207,7 +207,7 @@ func (rw *ResponseRecorder) Result() *http.Response { if trailers, ok := rw.snapHeader["Trailer"]; ok { res.Trailer = make(http.Header, len(trailers)) for _, k := range trailers { - for _, k := range strings.Split(k, ",") { + for k := range strings.SplitSeq(k, ",") { k = http.CanonicalHeaderKey(textproto.TrimString(k)) if !httpguts.ValidTrailerHeader(k) { // Ignore since forbidden by RFC 7230, section 4.1.2. diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go index d64d2fc3a18ee3..15e968470815d4 100644 --- a/src/net/http/httputil/reverseproxy.go +++ b/src/net/http/httputil/reverseproxy.go @@ -577,7 +577,7 @@ func shouldPanicOnCopyError(req *http.Request) bool { func removeHopByHopHeaders(h http.Header) { // RFC 7230, section 6.1: Remove headers listed in the "Connection" header. for _, f := range h["Connection"] { - for _, sf := range strings.Split(f, ",") { + for sf := range strings.SplitSeq(f, ",") { if sf = textproto.TrimString(sf); sf != "" { h.Del(sf) } diff --git a/src/net/http/httputil/reverseproxy_test.go b/src/net/http/httputil/reverseproxy_test.go index 2f9a5eec5cce4c..c618f6f19e3706 100644 --- a/src/net/http/httputil/reverseproxy_test.go +++ b/src/net/http/httputil/reverseproxy_test.go @@ -197,7 +197,7 @@ func TestReverseProxyStripHeadersPresentInConnection(t *testing.T) { c := r.Header["Connection"] var cf []string for _, f := range c { - for _, sf := range strings.Split(f, ",") { + for sf := range strings.SplitSeq(f, ",") { if sf = strings.TrimSpace(sf); sf != "" { cf = append(cf, sf) } diff --git a/src/net/http/internal/httpcommon/httpcommon.go b/src/net/http/internal/httpcommon/httpcommon.go new file mode 100644 index 00000000000000..c9f17785726f98 --- /dev/null +++ b/src/net/http/internal/httpcommon/httpcommon.go @@ -0,0 +1,532 @@ +// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. +//go:generate bundle -prefix= -o=httpcommon.go golang.org/x/net/internal/httpcommon + +package httpcommon + +import ( + "context" + "errors" + "fmt" + "net/http/httptrace" + "net/textproto" + "net/url" + "sort" + "strconv" + "strings" + "sync" + + "golang.org/x/net/http/httpguts" + "golang.org/x/net/http2/hpack" +) + +// The HTTP protocols are defined in terms of ASCII, not Unicode. This file +// contains helper functions which may use Unicode-aware functions which would +// otherwise be unsafe and could introduce vulnerabilities if used improperly. + +// asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t +// are equal, ASCII-case-insensitively. +func asciiEqualFold(s, t string) bool { + if len(s) != len(t) { + return false + } + for i := 0; i < len(s); i++ { + if lower(s[i]) != lower(t[i]) { + return false + } + } + return true +} + +// lower returns the ASCII lowercase version of b. +func lower(b byte) byte { + if 'A' <= b && b <= 'Z' { + return b + ('a' - 'A') + } + return b +} + +// isASCIIPrint returns whether s is ASCII and printable according to +// https://tools.ietf.org/html/rfc20#section-4.2. +func isASCIIPrint(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] < ' ' || s[i] > '~' { + return false + } + } + return true +} + +// asciiToLower returns the lowercase version of s if s is ASCII and printable, +// and whether or not it was. +func asciiToLower(s string) (lower string, ok bool) { + if !isASCIIPrint(s) { + return "", false + } + return strings.ToLower(s), true +} + +var ( + commonBuildOnce sync.Once + commonLowerHeader map[string]string // Go-Canonical-Case -> lower-case + commonCanonHeader map[string]string // lower-case -> Go-Canonical-Case +) + +func buildCommonHeaderMapsOnce() { + commonBuildOnce.Do(buildCommonHeaderMaps) +} + +func buildCommonHeaderMaps() { + common := []string{ + "accept", + "accept-charset", + "accept-encoding", + "accept-language", + "accept-ranges", + "age", + "access-control-allow-credentials", + "access-control-allow-headers", + "access-control-allow-methods", + "access-control-allow-origin", + "access-control-expose-headers", + "access-control-max-age", + "access-control-request-headers", + "access-control-request-method", + "allow", + "authorization", + "cache-control", + "content-disposition", + "content-encoding", + "content-language", + "content-length", + "content-location", + "content-range", + "content-type", + "cookie", + "date", + "etag", + "expect", + "expires", + "from", + "host", + "if-match", + "if-modified-since", + "if-none-match", + "if-unmodified-since", + "last-modified", + "link", + "location", + "max-forwards", + "origin", + "proxy-authenticate", + "proxy-authorization", + "range", + "referer", + "refresh", + "retry-after", + "server", + "set-cookie", + "strict-transport-security", + "trailer", + "transfer-encoding", + "user-agent", + "vary", + "via", + "www-authenticate", + "x-forwarded-for", + "x-forwarded-proto", + } + commonLowerHeader = make(map[string]string, len(common)) + commonCanonHeader = make(map[string]string, len(common)) + for _, v := range common { + chk := textproto.CanonicalMIMEHeaderKey(v) + commonLowerHeader[chk] = v + commonCanonHeader[v] = chk + } +} + +// LowerHeader returns the lowercase form of a header name, +// used on the wire for HTTP/2 and HTTP/3 requests. +func LowerHeader(v string) (lower string, ascii bool) { + buildCommonHeaderMapsOnce() + if s, ok := commonLowerHeader[v]; ok { + return s, true + } + return asciiToLower(v) +} + +// CanonicalHeader canonicalizes a header name. (For example, "host" becomes "Host".) +func CanonicalHeader(v string) string { + buildCommonHeaderMapsOnce() + if s, ok := commonCanonHeader[v]; ok { + return s + } + return textproto.CanonicalMIMEHeaderKey(v) +} + +// CachedCanonicalHeader returns the canonical form of a well-known header name. +func CachedCanonicalHeader(v string) (string, bool) { + buildCommonHeaderMapsOnce() + s, ok := commonCanonHeader[v] + return s, ok +} + +var ( + ErrRequestHeaderListSize = errors.New("request header list larger than peer's advertised limit") +) + +// Request is a subset of http.Request. +// It'd be simpler to pass an *http.Request, of course, but we can't depend on net/http +// without creating a dependency cycle. +type Request struct { + URL *url.URL + Method string + Host string + Header map[string][]string + Trailer map[string][]string + ActualContentLength int64 // 0 means 0, -1 means unknown +} + +// EncodeHeadersParam is parameters to EncodeHeaders. +type EncodeHeadersParam struct { + Request Request + + // AddGzipHeader indicates that an "accept-encoding: gzip" header should be + // added to the request. + AddGzipHeader bool + + // PeerMaxHeaderListSize, when non-zero, is the peer's MAX_HEADER_LIST_SIZE setting. + PeerMaxHeaderListSize uint64 + + // DefaultUserAgent is the User-Agent header to send when the request + // neither contains a User-Agent nor disables it. + DefaultUserAgent string +} + +// EncodeHeadersParam is the result of EncodeHeaders. +type EncodeHeadersResult struct { + HasBody bool + HasTrailers bool +} + +// EncodeHeaders constructs request headers common to HTTP/2 and HTTP/3. +// It validates a request and calls headerf with each pseudo-header and header +// for the request. +// The headerf function is called with the validated, canonicalized header name. +func EncodeHeaders(ctx context.Context, param EncodeHeadersParam, headerf func(name, value string)) (res EncodeHeadersResult, _ error) { + req := param.Request + + // Check for invalid connection-level headers. + if err := checkConnHeaders(req.Header); err != nil { + return res, err + } + + if req.URL == nil { + return res, errors.New("Request.URL is nil") + } + + host := req.Host + if host == "" { + host = req.URL.Host + } + host, err := httpguts.PunycodeHostPort(host) + if err != nil { + return res, err + } + if !httpguts.ValidHostHeader(host) { + return res, errors.New("invalid Host header") + } + + // isNormalConnect is true if this is a non-extended CONNECT request. + isNormalConnect := false + var protocol string + if vv := req.Header[":protocol"]; len(vv) > 0 { + protocol = vv[0] + } + if req.Method == "CONNECT" && protocol == "" { + isNormalConnect = true + } else if protocol != "" && req.Method != "CONNECT" { + return res, errors.New("invalid :protocol header in non-CONNECT request") + } + + // Validate the path, except for non-extended CONNECT requests which have no path. + var path string + if !isNormalConnect { + path = req.URL.RequestURI() + if !validPseudoPath(path) { + orig := path + path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host) + if !validPseudoPath(path) { + if req.URL.Opaque != "" { + return res, fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque) + } else { + return res, fmt.Errorf("invalid request :path %q", orig) + } + } + } + } + + // Check for any invalid headers+trailers and return an error before we + // potentially pollute our hpack state. (We want to be able to + // continue to reuse the hpack encoder for future requests) + if err := validateHeaders(req.Header); err != "" { + return res, fmt.Errorf("invalid HTTP header %s", err) + } + if err := validateHeaders(req.Trailer); err != "" { + return res, fmt.Errorf("invalid HTTP trailer %s", err) + } + + trailers, err := commaSeparatedTrailers(req.Trailer) + if err != nil { + return res, err + } + + enumerateHeaders := func(f func(name, value string)) { + // 8.1.2.3 Request Pseudo-Header Fields + // The :path pseudo-header field includes the path and query parts of the + // target URI (the path-absolute production and optionally a '?' character + // followed by the query production, see Sections 3.3 and 3.4 of + // [RFC3986]). + f(":authority", host) + m := req.Method + if m == "" { + m = "GET" + } + f(":method", m) + if !isNormalConnect { + f(":path", path) + f(":scheme", req.URL.Scheme) + } + if protocol != "" { + f(":protocol", protocol) + } + if trailers != "" { + f("trailer", trailers) + } + + var didUA bool + for k, vv := range req.Header { + if asciiEqualFold(k, "host") || asciiEqualFold(k, "content-length") { + // Host is :authority, already sent. + // Content-Length is automatic, set below. + continue + } else if asciiEqualFold(k, "connection") || + asciiEqualFold(k, "proxy-connection") || + asciiEqualFold(k, "transfer-encoding") || + asciiEqualFold(k, "upgrade") || + asciiEqualFold(k, "keep-alive") { + // Per 8.1.2.2 Connection-Specific Header + // Fields, don't send connection-specific + // fields. We have already checked if any + // are error-worthy so just ignore the rest. + continue + } else if asciiEqualFold(k, "user-agent") { + // Match Go's http1 behavior: at most one + // User-Agent. If set to nil or empty string, + // then omit it. Otherwise if not mentioned, + // include the default (below). + didUA = true + if len(vv) < 1 { + continue + } + vv = vv[:1] + if vv[0] == "" { + continue + } + } else if asciiEqualFold(k, "cookie") { + // Per 8.1.2.5 To allow for better compression efficiency, the + // Cookie header field MAY be split into separate header fields, + // each with one or more cookie-pairs. + for _, v := range vv { + for { + p := strings.IndexByte(v, ';') + if p < 0 { + break + } + f("cookie", v[:p]) + p++ + // strip space after semicolon if any. + for p+1 <= len(v) && v[p] == ' ' { + p++ + } + v = v[p:] + } + if len(v) > 0 { + f("cookie", v) + } + } + continue + } else if k == ":protocol" { + // :protocol pseudo-header was already sent above. + continue + } + + for _, v := range vv { + f(k, v) + } + } + if shouldSendReqContentLength(req.Method, req.ActualContentLength) { + f("content-length", strconv.FormatInt(req.ActualContentLength, 10)) + } + if param.AddGzipHeader { + f("accept-encoding", "gzip") + } + if !didUA { + f("user-agent", param.DefaultUserAgent) + } + } + + // Do a first pass over the headers counting bytes to ensure + // we don't exceed cc.peerMaxHeaderListSize. This is done as a + // separate pass before encoding the headers to prevent + // modifying the hpack state. + if param.PeerMaxHeaderListSize > 0 { + hlSize := uint64(0) + enumerateHeaders(func(name, value string) { + hf := hpack.HeaderField{Name: name, Value: value} + hlSize += uint64(hf.Size()) + }) + + if hlSize > param.PeerMaxHeaderListSize { + return res, ErrRequestHeaderListSize + } + } + + trace := httptrace.ContextClientTrace(ctx) + + // Header list size is ok. Write the headers. + enumerateHeaders(func(name, value string) { + name, ascii := LowerHeader(name) + if !ascii { + // Skip writing invalid headers. Per RFC 7540, Section 8.1.2, header + // field names have to be ASCII characters (just as in HTTP/1.x). + return + } + + headerf(name, value) + + if trace != nil && trace.WroteHeaderField != nil { + trace.WroteHeaderField(name, []string{value}) + } + }) + + res.HasBody = req.ActualContentLength != 0 + res.HasTrailers = trailers != "" + return res, nil +} + +// IsRequestGzip reports whether we should add an Accept-Encoding: gzip header +// for a request. +func IsRequestGzip(method string, header map[string][]string, disableCompression bool) bool { + // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? + if !disableCompression && + len(header["Accept-Encoding"]) == 0 && + len(header["Range"]) == 0 && + method != "HEAD" { + // Request gzip only, not deflate. Deflate is ambiguous and + // not as universally supported anyway. + // See: https://zlib.net/zlib_faq.html#faq39 + // + // Note that we don't request this for HEAD requests, + // due to a bug in nginx: + // http://trac.nginx.org/nginx/ticket/358 + // https://golang.org/issue/5522 + // + // We don't request gzip if the request is for a range, since + // auto-decoding a portion of a gzipped document will just fail + // anyway. See https://golang.org/issue/8923 + return true + } + return false +} + +// checkConnHeaders checks whether req has any invalid connection-level headers. +// +// https://www.rfc-editor.org/rfc/rfc9114.html#section-4.2-3 +// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.2.2-1 +// +// Certain headers are special-cased as okay but not transmitted later. +// For example, we allow "Transfer-Encoding: chunked", but drop the header when encoding. +func checkConnHeaders(h map[string][]string) error { + if vv := h["Upgrade"]; len(vv) > 0 && (vv[0] != "" && vv[0] != "chunked") { + return fmt.Errorf("invalid Upgrade request header: %q", vv) + } + if vv := h["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") { + return fmt.Errorf("invalid Transfer-Encoding request header: %q", vv) + } + if vv := h["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !asciiEqualFold(vv[0], "close") && !asciiEqualFold(vv[0], "keep-alive")) { + return fmt.Errorf("invalid Connection request header: %q", vv) + } + return nil +} + +func commaSeparatedTrailers(trailer map[string][]string) (string, error) { + keys := make([]string, 0, len(trailer)) + for k := range trailer { + k = CanonicalHeader(k) + switch k { + case "Transfer-Encoding", "Trailer", "Content-Length": + return "", fmt.Errorf("invalid Trailer key %q", k) + } + keys = append(keys, k) + } + if len(keys) > 0 { + sort.Strings(keys) + return strings.Join(keys, ","), nil + } + return "", nil +} + +// validPseudoPath reports whether v is a valid :path pseudo-header +// value. It must be either: +// +// - a non-empty string starting with '/' +// - the string '*', for OPTIONS requests. +// +// For now this is only used a quick check for deciding when to clean +// up Opaque URLs before sending requests from the Transport. +// See golang.org/issue/16847 +// +// We used to enforce that the path also didn't start with "//", but +// Google's GFE accepts such paths and Chrome sends them, so ignore +// that part of the spec. See golang.org/issue/19103. +func validPseudoPath(v string) bool { + return (len(v) > 0 && v[0] == '/') || v == "*" +} + +func validateHeaders(hdrs map[string][]string) string { + for k, vv := range hdrs { + if !httpguts.ValidHeaderFieldName(k) && k != ":protocol" { + return fmt.Sprintf("name %q", k) + } + for _, v := range vv { + if !httpguts.ValidHeaderFieldValue(v) { + // Don't include the value in the error, + // because it may be sensitive. + return fmt.Sprintf("value for header %q", k) + } + } + } + return "" +} + +// shouldSendReqContentLength reports whether we should send +// a "content-length" request header. This logic is basically a copy of the net/http +// transferWriter.shouldSendContentLength. +// The contentLength is the corrected contentLength (so 0 means actually 0, not unknown). +// -1 means unknown. +func shouldSendReqContentLength(method string, contentLength int64) bool { + if contentLength > 0 { + return true + } + if contentLength < 0 { + return false + } + // For zero bodies, whether we send a content-length depends on the method. + // It also kinda doesn't matter for http2 either way, with END_STREAM. + switch method { + case "POST", "PUT", "PATCH": + return true + default: + return false + } +} diff --git a/src/net/http/internal/testcert/testcert.go b/src/net/http/internal/testcert/testcert.go index d510e791d617cb..78ce42e2282679 100644 --- a/src/net/http/internal/testcert/testcert.go +++ b/src/net/http/internal/testcert/testcert.go @@ -10,56 +10,56 @@ import "strings" // LocalhostCert is a PEM-encoded TLS cert with SAN IPs // "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT. // generated from src/crypto/tls: -// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h +// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com,*.example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var LocalhostCert = []byte(`-----BEGIN CERTIFICATE----- -MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS +MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r -bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U -aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P -YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk -POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu -h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE +MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u +FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/ +jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH +DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD +qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl +U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv -bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI -5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv -cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2 -+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B -grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK -5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/ -WkBKOclmOV2xlTVuPw== +DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv +bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG +9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu +LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR +Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5 +2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO +6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL +rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg== -----END CERTIFICATE-----`) // LocalhostKey is the private key for LocalhostCert. var LocalhostKey = []byte(testingKey(`-----BEGIN RSA TESTING KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDoZtrm0dXV0Aqi -4Bpc7f95sNRTiu/AJSD8I1onY9PnEsPg3VVxvytsVJbYdcqr4w99V3AgpH/UNzMS -gAZ/8lZBNbsSDOVesJ3euVqMRfYPvd9pYl6QPRRpSDPm+2tNdn3QFAvta9EgJ3sW -URnoU85w+W6aLI2bNSq3AaE771p3VbkGolpEjo9h+i42TBHo1rhPNKPkGupR8/QX -AOLMpInRdeaHyDwb2a3DE5I3dG7VAVzrVfJ6W6Q84YoFX+rpEE2SVM17SAjy6xQy -VjKgLvK2mk0xbtfa+h0B6VK7bmODHZqeP18NVm6HsBcXn7iclLgAC3SfWU1jucZK -x1lqzw9tAgMBAAECggEABWzxS1Y2wckblnXY57Z+sl6YdmLV+gxj2r8Qib7g4ZIk -lIlWR1OJNfw7kU4eryib4fc6nOh6O4AWZyYqAK6tqNQSS/eVG0LQTLTTEldHyVJL -dvBe+MsUQOj4nTndZW+QvFzbcm2D8lY5n2nBSxU5ypVoKZ1EqQzytFcLZpTN7d89 -EPj0qDyrV4NZlWAwL1AygCwnlwhMQjXEalVF1ylXwU3QzyZ/6MgvF6d3SSUlh+sq -XefuyigXw484cQQgbzopv6niMOmGP3of+yV4JQqUSb3IDmmT68XjGd2Dkxl4iPki -6ZwXf3CCi+c+i/zVEcufgZ3SLf8D99kUGE7v7fZ6AQKBgQD1ZX3RAla9hIhxCf+O -3D+I1j2LMrdjAh0ZKKqwMR4JnHX3mjQI6LwqIctPWTU8wYFECSh9klEclSdCa64s -uI/GNpcqPXejd0cAAdqHEEeG5sHMDt0oFSurL4lyud0GtZvwlzLuwEweuDtvT9cJ -Wfvl86uyO36IW8JdvUprYDctrQKBgQDycZ697qutBieZlGkHpnYWUAeImVA878sJ -w44NuXHvMxBPz+lbJGAg8Cn8fcxNAPqHIraK+kx3po8cZGQywKHUWsxi23ozHoxo -+bGqeQb9U661TnfdDspIXia+xilZt3mm5BPzOUuRqlh4Y9SOBpSWRmEhyw76w4ZP -OPxjWYAgwQKBgA/FehSYxeJgRjSdo+MWnK66tjHgDJE8bYpUZsP0JC4R9DL5oiaA -brd2fI6Y+SbyeNBallObt8LSgzdtnEAbjIH8uDJqyOmknNePRvAvR6mP4xyuR+Bv -m+Lgp0DMWTw5J9CKpydZDItc49T/mJ5tPhdFVd+am0NAQnmr1MCZ6nHxAoGABS3Y -LkaC9FdFUUqSU8+Chkd/YbOkuyiENdkvl6t2e52jo5DVc1T7mLiIrRQi4SI8N9bN -/3oJWCT+uaSLX2ouCtNFunblzWHBrhxnZzTeqVq4SLc8aESAnbslKL4i8/+vYZlN -s8xtiNcSvL+lMsOBORSXzpj/4Ot8WwTkn1qyGgECgYBKNTypzAHeLE6yVadFp3nQ -Ckq9yzvP/ib05rvgbvrne00YeOxqJ9gtTrzgh7koqJyX1L4NwdkEza4ilDWpucn0 -xiUZS4SoaJq6ZvcBYS62Yr1t8n09iG47YL8ibgtmH3L+svaotvpVxVK+d7BLevA/ -ZboOWVe3icTy64BT3OQhmg== +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDFyXr1E4l3GM34 +wlmdsWtjHJCigAMKwnpUOS4zI1AiLH8eTXk2T+4XIFfUx775oSkaZjdEhjh9S8Qu +pP+yu8AexNfBVVK20xxjylwAWZdKqjfHgy5RMb+MJfdV+2PSvcQzkzwiZjWvMD+O +pNMsmDTSsP4Oa4MAFypC+hfD9FXzDXJNGLkE+gcMUP8BZO39iAy+TWXZir/EjxVs +xQimMGgZfFaxJ69DmLazWaT3/JnO7RiynW1OXMOo49rjKwWMGK11eLB/GPG2/mde +o4I/muF4o7SxYuTR960ynU5XklIkwAnDpzZkySVTZYyoASlGN0T+8d8i42D7IZpF +GojTs1lFAgMBAAECggEAIYthUi1lFBDd5gG4Rzlu+BlBIn5JhcqkCqLEBiJIFfOr +/4yuMRrvS3bNzqWt6xJ9MSAC4ZlN/VobRLnxL/QNymoiGYUKCT3Ww8nvPpPzR9OE +sE68TUL9tJw/zZJcRMKwgvrGqSLimfq53MxxkE+kLdOc0v9C8YH8Re26mB5ZcWYa +7YFyZQpKsQYnsmu/05cMbpOQrQWhtmIqRoyn8mG/par2s3NzjtpSE9NINyz26uFc +k/3ovFJQIHkUmTS7KHD3BgY5vuCqP98HramYnOysJ0WoYgvSDNCWw3037s5CCwJT +gCKuM+Ow6liFrj83RrdKBpm5QUGjfNpYP31o+QNP4QKBgQDSrUQ2XdgtAnibAV7u +7kbxOxro0EhIKso0Y/6LbDQgcXgxLqltkmeqZgG8nC3Z793lhlSasz2snhzzooV5 +5fTy1y8ikXqjhG0nNkInFyOhsI0auE28CFoDowaQd+5cmCatpN4Grqo5PNRXxm1w +HktfPEgoP11NNCFHvvN5fEKbbQKBgQDwVlOaV20IvW3IPq7cXZyiyabouFF9eTRo +VJka1Uv+JtyvL2P0NKkjYHOdN8gRblWqxQtJoTNk020rVA4UP1heiXALy50gvj/p +hMcybPTLYSPOhAGx838KIcvGR5oskP1aUCmFbFQzGELxhJ9diVVjxUtbG2DuwPKd +tD9TLxT2OQKBgQCcdlHSjp+dzdgERmBa0ludjGfPv9/uuNizUBAbO6D690psPFtY +JQMYaemgSd1DngEOFVWADt4e9M5Lose+YCoqr+UxpxmNlyv5kzJOFcFAs/4XeglB +PHKdgNW/NVKxMc6H54l9LPr+x05sYdGlEtqnP/3W5jhEvhJ5Vjc8YiyVgQKBgQCl +zwjyrGo+42GACy7cPYE5FeIfIDqoVByB9guC5bD98JXEDu/opQQjsgFRcBCJZhOY +M0UsURiB8ROaFu13rpQq9KrmmF0ZH+g8FSzQbzcbsTLg4VXCDXmR5esOKowFPypr +Sm667BfTAGP++D5ya7MLmCv6+RKQ5XD8uEQQAaV2kQKBgAD8qeJuWIXZT0VKkQrn +nIhgtzGERF/6sZdQGW2LxTbUDWG74AfFkkEbeBfwEkCZXY/xmnYqYABhvlSex8jU +supU6Eea21esIxIub2zv/Np0ojUb6rlqTPS4Ox1E27D787EJ3VOXpriSD10vyNnZ +jel6uj2FOP9g54s+GzlSVg/T -----END RSA TESTING KEY-----`)) func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") } diff --git a/src/net/http/main_test.go b/src/net/http/main_test.go index 4c1832071704e2..0c58a94f20fc24 100644 --- a/src/net/http/main_test.go +++ b/src/net/http/main_test.go @@ -31,7 +31,7 @@ func TestMain(m *testing.M) { func interestingGoroutines() (gs []string) { buf := make([]byte, 2<<20) buf = buf[:runtime.Stack(buf, true)] - for _, g := range strings.Split(string(buf), "\n\n") { + for g := range strings.SplitSeq(string(buf), "\n\n") { _, stack, _ := strings.Cut(g, "\n") stack = strings.TrimSpace(stack) if stack == "" || diff --git a/src/net/http/request.go b/src/net/http/request.go index 686d53345aebd9..434c1640f3941d 100644 --- a/src/net/http/request.go +++ b/src/net/http/request.go @@ -873,9 +873,9 @@ func NewRequest(method, url string, body io.Reader) (*Request, error) { // // NewRequestWithContext returns a Request suitable for use with // [Client.Do] or [Transport.RoundTrip]. To create a request for use with -// testing a Server Handler, either use the [NewRequest] function in the -// net/http/httptest package, use [ReadRequest], or manually update the -// Request fields. For an outgoing client request, the context +// testing a Server Handler, either use the [net/http/httptest.NewRequest] function, +// use [ReadRequest], or manually update the Request fields. +// For an outgoing client request, the context // controls the entire lifetime of a request and its response: // obtaining a connection, sending the request, and reading the // response headers and body. See the Request type's documentation for diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go index 0c46b1ecc3e0b1..89fcbd1329b1f5 100644 --- a/src/net/http/serve_test.go +++ b/src/net/http/serve_test.go @@ -4302,19 +4302,6 @@ func TestResponseWriterWriteString(t *testing.T) { } } -func TestAppendTime(t *testing.T) { - var b [len(TimeFormat)]byte - t1 := time.Date(2013, 9, 21, 15, 41, 0, 0, time.FixedZone("CEST", 2*60*60)) - res := ExportAppendTime(b[:0], t1) - t2, err := ParseTime(string(res)) - if err != nil { - t.Fatalf("Error parsing time: %s", err) - } - if !t1.Equal(t2) { - t.Fatalf("Times differ; expected: %v, got %v (%s)", t1, t2, string(res)) - } -} - func TestServerConnState(t *testing.T) { run(t, testServerConnState, []testMode{http1Mode}) } func testServerConnState(t *testing.T, mode testMode) { handler := map[string]func(w ResponseWriter, r *Request){ @@ -7141,10 +7128,6 @@ func testHeadBody(t *testing.T, mode testMode, chunked bool, method string) { // or disabled when the header is set to nil. func TestDisableContentLength(t *testing.T) { run(t, testDisableContentLength) } func testDisableContentLength(t *testing.T, mode testMode) { - if mode == http2Mode { - t.Skip("skipping until h2_bundle.go is updated; see https://go-review.googlesource.com/c/net/+/471535") - } - noCL := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) { w.Header()["Content-Length"] = nil // disable the default Content-Length response fmt.Fprintf(w, "OK") diff --git a/src/net/http/server.go b/src/net/http/server.go index 1e8e1437d26832..439efa0c753068 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -991,28 +991,6 @@ func (ecr *expectContinueReader) Close() error { // For parsing this time format, see [ParseTime]. const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" -// appendTime is a non-allocating version of []byte(t.UTC().Format(TimeFormat)) -func appendTime(b []byte, t time.Time) []byte { - const days = "SunMonTueWedThuFriSat" - const months = "JanFebMarAprMayJunJulAugSepOctNovDec" - - t = t.UTC() - yy, mm, dd := t.Date() - hh, mn, ss := t.Clock() - day := days[3*t.Weekday():] - mon := months[3*(mm-1):] - - return append(b, - day[0], day[1], day[2], ',', ' ', - byte('0'+dd/10), byte('0'+dd%10), ' ', - mon[0], mon[1], mon[2], ' ', - byte('0'+yy/1000), byte('0'+(yy/100)%10), byte('0'+(yy/10)%10), byte('0'+yy%10), ' ', - byte('0'+hh/10), byte('0'+hh%10), ':', - byte('0'+mn/10), byte('0'+mn%10), ':', - byte('0'+ss/10), byte('0'+ss%10), ' ', - 'G', 'M', 'T') -} - var errTooLarge = errors.New("http: request too large") // Read next request from connection. @@ -1506,7 +1484,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { } if !header.has("Date") { - setHeader.date = appendTime(cw.res.dateBuf[:0], time.Now()) + setHeader.date = time.Now().UTC().AppendFormat(cw.res.dateBuf[:0], TimeFormat) } if hasCL && hasTE && te != "identity" { @@ -1590,7 +1568,7 @@ func foreachHeaderElement(v string, fn func(string)) { fn(v) return } - for _, f := range strings.Split(v, ",") { + for f := range strings.SplitSeq(v, ",") { if f = textproto.TrimString(f); f != "" { fn(f) } diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go index 2963255b874378..7166c112792334 100644 --- a/src/net/http/transport_test.go +++ b/src/net/http/transport_test.go @@ -2034,7 +2034,7 @@ func (d *countingDialer) DialContext(ctx context.Context, network, address strin d.total++ d.live++ - runtime.SetFinalizer(counted, d.decrement) + runtime.AddCleanup(counted, func(dd *countingDialer) { dd.decrement(nil) }, d) return counted, nil } @@ -2106,7 +2106,7 @@ func (cc *contextCounter) Track(ctx context.Context) context.Context { cc.mu.Lock() defer cc.mu.Unlock() cc.live++ - runtime.SetFinalizer(counted, cc.decrement) + runtime.AddCleanup(counted, func(c *contextCounter) { cc.decrement(nil) }, cc) return counted } @@ -5500,7 +5500,9 @@ timeoutLoop: return false } } - res.Body.Close() + if err == nil { + res.Body.Close() + } conns := idleConns() if len(conns) != 1 { if len(conns) == 0 { diff --git a/src/net/interface.go b/src/net/interface.go index 74bb4f0e1c6122..b6057780c4a98f 100644 --- a/src/net/interface.go +++ b/src/net/interface.go @@ -42,7 +42,7 @@ var ( type Interface struct { Index int // positive integer that starts at one, zero is never used MTU int // maximum transmission unit - Name string // e.g., "en0", "lo0", "eth0.100" + Name string // e.g., "en0", "lo0", "eth0.100"; may be the empty string HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form Flags Flags // e.g., FlagUp, FlagLoopback, FlagMulticast } @@ -221,9 +221,11 @@ func (zc *ipv6ZoneCache) update(ift []Interface, force bool) (updated bool) { zc.toIndex = make(map[string]int, len(ift)) zc.toName = make(map[int]string, len(ift)) for _, ifi := range ift { - zc.toIndex[ifi.Name] = ifi.Index - if _, ok := zc.toName[ifi.Index]; !ok { - zc.toName[ifi.Index] = ifi.Name + if ifi.Name != "" { + zc.toIndex[ifi.Name] = ifi.Index + if _, ok := zc.toName[ifi.Index]; !ok { + zc.toName[ifi.Index] = ifi.Name + } } } return true diff --git a/src/net/interface_bsd.go b/src/net/interface_bsd.go index 9b2b42addbb609..0861610f168996 100644 --- a/src/net/interface_bsd.go +++ b/src/net/interface_bsd.go @@ -7,9 +7,8 @@ package net import ( + "internal/routebsd" "syscall" - - "golang.org/x/net/route" ) // If the ifindex is zero, interfaceTable returns mappings of all @@ -28,23 +27,18 @@ func interfaceTable(ifindex int) ([]Interface, error) { n = 0 for _, m := range msgs { switch m := m.(type) { - case *route.InterfaceMessage: + case *routebsd.InterfaceMessage: if ifindex != 0 && ifindex != m.Index { continue } ift[n].Index = m.Index ift[n].Name = m.Name ift[n].Flags = linkFlags(m.Flags) - if sa, ok := m.Addrs[syscall.RTAX_IFP].(*route.LinkAddr); ok && len(sa.Addr) > 0 { + if sa, ok := m.Addrs[syscall.RTAX_IFP].(*routebsd.LinkAddr); ok && len(sa.Addr) > 0 { ift[n].HardwareAddr = make([]byte, len(sa.Addr)) copy(ift[n].HardwareAddr, sa.Addr) } - for _, sys := range m.Sys() { - if imx, ok := sys.(*route.InterfaceMetrics); ok { - ift[n].MTU = imx.MTU - break - } - } + ift[n].MTU = m.MTU() n++ if ifindex == m.Index { return ift[:n], nil @@ -92,27 +86,35 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) { ifat := make([]Addr, 0, len(msgs)) for _, m := range msgs { switch m := m.(type) { - case *route.InterfaceAddrMessage: + case *routebsd.InterfaceAddrMessage: if index != 0 && index != m.Index { continue } var mask IPMask switch sa := m.Addrs[syscall.RTAX_NETMASK].(type) { - case *route.Inet4Addr: - mask = IPv4Mask(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) - case *route.Inet6Addr: - mask = make(IPMask, IPv6len) - copy(mask, sa.IP[:]) + case *routebsd.InetAddr: + if sa.IP.Is4() { + a := sa.IP.As4() + mask = IPv4Mask(a[0], a[1], a[2], a[3]) + } else if sa.IP.Is6() { + a := sa.IP.As16() + mask = make(IPMask, IPv6len) + copy(mask, a[:]) + } } var ip IP switch sa := m.Addrs[syscall.RTAX_IFA].(type) { - case *route.Inet4Addr: - ip = IPv4(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) - case *route.Inet6Addr: - ip = make(IP, IPv6len) - copy(ip, sa.IP[:]) + case *routebsd.InetAddr: + if sa.IP.Is4() { + a := sa.IP.As4() + ip = IPv4(a[0], a[1], a[2], a[3]) + } else if sa.IP.Is6() { + a := sa.IP.As16() + ip = make(IP, IPv6len) + copy(ip, a[:]) + } } - if ip != nil && mask != nil { // NetBSD may contain route.LinkAddr + if ip != nil && mask != nil { // NetBSD may contain routebsd.LinkAddr ifat = append(ifat, &IPNet{IP: ip, Mask: mask}) } } diff --git a/src/net/interface_bsdvar.go b/src/net/interface_bsdvar.go index e9bea3d3798a06..dc6c293a8e4b1a 100644 --- a/src/net/interface_bsdvar.go +++ b/src/net/interface_bsdvar.go @@ -7,17 +7,12 @@ package net import ( + "internal/routebsd" "syscall" - - "golang.org/x/net/route" ) -func interfaceMessages(ifindex int) ([]route.Message, error) { - rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLIST, ifindex) - if err != nil { - return nil, err - } - return route.ParseRIB(syscall.NET_RT_IFLIST, rib) +func interfaceMessages(ifindex int) ([]routebsd.Message, error) { + return routebsd.FetchRIBMessages(syscall.NET_RT_IFLIST, ifindex) } // interfaceMulticastAddrTable returns addresses for a specific diff --git a/src/net/interface_darwin.go b/src/net/interface_darwin.go index bb4fd73a987670..95c44e68e63a1e 100644 --- a/src/net/interface_darwin.go +++ b/src/net/interface_darwin.go @@ -5,44 +5,39 @@ package net import ( + "internal/routebsd" "syscall" - - "golang.org/x/net/route" ) -func interfaceMessages(ifindex int) ([]route.Message, error) { - rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLIST, ifindex) - if err != nil { - return nil, err - } - return route.ParseRIB(syscall.NET_RT_IFLIST, rib) +func interfaceMessages(ifindex int) ([]routebsd.Message, error) { + return routebsd.FetchRIBMessages(syscall.NET_RT_IFLIST, ifindex) } // interfaceMulticastAddrTable returns addresses for a specific // interface. func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { - rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFLIST2, ifi.Index) - if err != nil { - return nil, err - } - msgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib) + msgs, err := routebsd.FetchRIBMessages(syscall.NET_RT_IFLIST2, ifi.Index) if err != nil { return nil, err } ifmat := make([]Addr, 0, len(msgs)) for _, m := range msgs { switch m := m.(type) { - case *route.InterfaceMulticastAddrMessage: + case *routebsd.InterfaceMulticastAddrMessage: if ifi.Index != m.Index { continue } var ip IP switch sa := m.Addrs[syscall.RTAX_IFA].(type) { - case *route.Inet4Addr: - ip = IPv4(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) - case *route.Inet6Addr: - ip = make(IP, IPv6len) - copy(ip, sa.IP[:]) + case *routebsd.InetAddr: + if sa.IP.Is4() { + a := sa.IP.As4() + ip = IPv4(a[0], a[1], a[2], a[3]) + } else if sa.IP.Is6() { + a := sa.IP.As16() + ip = make(IP, IPv6len) + copy(ip, a[:]) + } } if ip != nil { ifmat = append(ifmat, &IPAddr{IP: ip}) diff --git a/src/net/interface_freebsd.go b/src/net/interface_freebsd.go index 8536bd3cf6482b..301faa47baef79 100644 --- a/src/net/interface_freebsd.go +++ b/src/net/interface_freebsd.go @@ -5,44 +5,39 @@ package net import ( + "internal/routebsd" "syscall" - - "golang.org/x/net/route" ) -func interfaceMessages(ifindex int) ([]route.Message, error) { - rib, err := route.FetchRIB(syscall.AF_UNSPEC, route.RIBTypeInterface, ifindex) - if err != nil { - return nil, err - } - return route.ParseRIB(route.RIBTypeInterface, rib) +func interfaceMessages(ifindex int) ([]routebsd.Message, error) { + return routebsd.FetchRIBMessages(syscall.NET_RT_IFLIST, ifindex) } // interfaceMulticastAddrTable returns addresses for a specific // interface. func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { - rib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_IFMALIST, ifi.Index) - if err != nil { - return nil, err - } - msgs, err := route.ParseRIB(syscall.NET_RT_IFMALIST, rib) + msgs, err := routebsd.FetchRIBMessages(syscall.NET_RT_IFMALIST, ifi.Index) if err != nil { return nil, err } ifmat := make([]Addr, 0, len(msgs)) for _, m := range msgs { switch m := m.(type) { - case *route.InterfaceMulticastAddrMessage: + case *routebsd.InterfaceMulticastAddrMessage: if ifi.Index != m.Index { continue } var ip IP switch sa := m.Addrs[syscall.RTAX_IFA].(type) { - case *route.Inet4Addr: - ip = IPv4(sa.IP[0], sa.IP[1], sa.IP[2], sa.IP[3]) - case *route.Inet6Addr: - ip = make(IP, IPv6len) - copy(ip, sa.IP[:]) + case *routebsd.InetAddr: + if sa.IP.Is4() { + a := sa.IP.As4() + ip = IPv4(a[0], a[1], a[2], a[3]) + } else if sa.IP.Is6() { + a := sa.IP.As16() + ip = make(IP, IPv6len) + copy(ip, a[:]) + } } if ip != nil { ifmat = append(ifmat, &IPAddr{IP: ip}) diff --git a/src/net/interface_linux.go b/src/net/interface_linux.go index 9112ecc854c74c..7856dae8fc878b 100644 --- a/src/net/interface_linux.go +++ b/src/net/interface_linux.go @@ -129,22 +129,14 @@ func interfaceAddrTable(ifi *Interface) ([]Addr, error) { if err != nil { return nil, os.NewSyscallError("parsenetlinkmessage", err) } - var ift []Interface - if ifi == nil { - var err error - ift, err = interfaceTable(0) - if err != nil { - return nil, err - } - } - ifat, err := addrTable(ift, ifi, msgs) + ifat, err := addrTable(ifi, msgs) if err != nil { return nil, err } return ifat, nil } -func addrTable(ift []Interface, ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) { +func addrTable(ifi *Interface, msgs []syscall.NetlinkMessage) ([]Addr, error) { var ifat []Addr loop: for _, m := range msgs { @@ -153,14 +145,7 @@ loop: break loop case syscall.RTM_NEWADDR: ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) - if len(ift) != 0 || ifi.Index == int(ifam.Index) { - if len(ift) != 0 { - var err error - ifi, err = interfaceByIndex(ift, int(ifam.Index)) - if err != nil { - return nil, err - } - } + if ifi == nil || ifi.Index == int(ifam.Index) { attrs, err := syscall.ParseNetlinkRouteAttr(&m) if err != nil { return nil, os.NewSyscallError("parsenetlinkrouteattr", err) diff --git a/src/net/interface_test.go b/src/net/interface_test.go index a97d675e7e0bd6..72befca0d89c95 100644 --- a/src/net/interface_test.go +++ b/src/net/interface_test.go @@ -68,12 +68,14 @@ func TestInterfaces(t *testing.T) { t.Errorf("got %v; want %v", ifxi, ifi) } } - ifxn, err := InterfaceByName(ifi.Name) - if err != nil { - t.Fatal(err) - } - if !reflect.DeepEqual(ifxn, &ifi) { - t.Errorf("got %v; want %v", ifxn, ifi) + if ifi.Name != "" { + ifxn, err := InterfaceByName(ifi.Name) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(ifxn, &ifi) { + t.Errorf("got %v; want %v", ifxn, ifi) + } } t.Logf("%s: flags=%v index=%d mtu=%d hwaddr=%v", ifi.Name, ifi.Flags, ifi.Index, ifi.MTU, ifi.HardwareAddr) } diff --git a/src/net/lookup.go b/src/net/lookup.go index b04dfa23b9877b..f94fd8cefaab18 100644 --- a/src/net/lookup.go +++ b/src/net/lookup.go @@ -614,6 +614,9 @@ func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) { // LookupTXT returns the DNS TXT records for the given domain name. // +// If a DNS TXT record holds multiple strings, they are concatenated as a +// single string. +// // LookupTXT uses [context.Background] internally; to specify the context, use // [Resolver.LookupTXT]. func LookupTXT(name string) ([]string, error) { @@ -621,6 +624,9 @@ func LookupTXT(name string) ([]string, error) { } // LookupTXT returns the DNS TXT records for the given domain name. +// +// If a DNS TXT record holds multiple strings, they are concatenated as a +// single string. func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error) { return r.lookupTXT(ctx, name) } diff --git a/src/net/main_test.go b/src/net/main_test.go index e5767f7c7c12f6..66735962f10373 100644 --- a/src/net/main_test.go +++ b/src/net/main_test.go @@ -185,7 +185,7 @@ func runningGoroutines() []string { var gss []string b := make([]byte, 2<<20) b = b[:runtime.Stack(b, true)] - for _, s := range strings.Split(string(b), "\n\n") { + for s := range strings.SplitSeq(string(b), "\n\n") { _, stack, _ := strings.Cut(s, "\n") stack = strings.TrimSpace(stack) if !strings.Contains(stack, "created by net") { diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go index 50554c05c5eb2a..480e89dfd74945 100644 --- a/src/net/net_windows_test.go +++ b/src/net/net_windows_test.go @@ -240,8 +240,7 @@ func netshInterfaceIPShowInterface(ipver string, ifaces map[string]bool) error { //Metric : 10 //... var name string - lines := bytes.Split(out, []byte{'\r', '\n'}) - for _, line := range lines { + for line := range bytes.SplitSeq(out, []byte{'\r', '\n'}) { if bytes.HasPrefix(line, []byte("Interface ")) && bytes.HasSuffix(line, []byte(" Parameters")) { f := line[len("Interface "):] f = f[:len(f)-len(" Parameters")] @@ -330,8 +329,7 @@ func netshInterfaceIPv4ShowAddress(name string, netshOutput []byte) []string { addrs := make([]string, 0) var addr, subnetprefix string var processingOurInterface bool - lines := bytes.Split(netshOutput, []byte{'\r', '\n'}) - for _, line := range lines { + for line := range bytes.SplitSeq(netshOutput, []byte{'\r', '\n'}) { if !processingOurInterface { if !bytes.HasPrefix(line, []byte("Configuration for interface")) { continue @@ -398,8 +396,7 @@ func netshInterfaceIPv6ShowAddress(name string, netshOutput []byte) []string { // TODO: need to test ipv6 netmask too, but netsh does not outputs it var addr string addrs := make([]string, 0) - lines := bytes.Split(netshOutput, []byte{'\r', '\n'}) - for _, line := range lines { + for line := range bytes.SplitSeq(netshOutput, []byte{'\r', '\n'}) { if addr != "" { if len(line) == 0 { addr = "" @@ -584,8 +581,7 @@ func TestInterfaceHardwareAddrWithGetmac(t *testing.T) { want[cname] = addr group = make(map[string]string) } - lines := bytes.Split(out, []byte{'\r', '\n'}) - for _, line := range lines { + for line := range bytes.SplitSeq(out, []byte{'\r', '\n'}) { if len(line) == 0 { processGroup() continue diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go index 1771726a932eda..4233a426fefb1f 100644 --- a/src/net/rpc/server.go +++ b/src/net/rpc/server.go @@ -4,7 +4,11 @@ /* Package rpc provides access to the exported methods of an object across a -network or other I/O connection. A server registers an object, making it visible +network or other I/O connection. + +The net/rpc package is frozen and is not accepting new features. + +A server registers an object, making it visible as a service with the name of the type of the object. After registration, exported methods of the object will be accessible remotely. A server may register multiple objects (services) of different types but it is an error to register multiple @@ -121,8 +125,6 @@ or A server implementation will often provide a simple, type-safe wrapper for the client. - -The net/rpc package is frozen and is not accepting new features. */ package rpc diff --git a/src/net/sendfile_unix_test.go b/src/net/sendfile_unix_test.go new file mode 100644 index 00000000000000..79fb23b31010d5 --- /dev/null +++ b/src/net/sendfile_unix_test.go @@ -0,0 +1,86 @@ +// Copyright 2024 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. + +//go:build unix + +package net + +import ( + "internal/testpty" + "io" + "os" + "sync" + "syscall" + "testing" +) + +// Issue 70763: test that we don't fail on sendfile from a tty. +func TestCopyFromTTY(t *testing.T) { + pty, ttyName, err := testpty.Open() + if err != nil { + t.Skipf("skipping test because pty open failed: %v", err) + } + defer pty.Close() + + // Use syscall.Open so that the tty is blocking. + ttyFD, err := syscall.Open(ttyName, syscall.O_RDWR, 0) + if err != nil { + t.Skipf("skipping test because tty open failed: %v", err) + } + defer syscall.Close(ttyFD) + + tty := os.NewFile(uintptr(ttyFD), "tty") + defer tty.Close() + + ln := newLocalListener(t, "tcp") + defer ln.Close() + + ch := make(chan bool) + + const data = "data\n" + + var wg sync.WaitGroup + defer wg.Wait() + + wg.Add(1) + go func() { + defer wg.Done() + conn, err := ln.Accept() + if err != nil { + t.Error(err) + return + } + defer conn.Close() + + buf := make([]byte, len(data)) + if _, err := io.ReadFull(conn, buf); err != nil { + t.Error(err) + } + + ch <- true + }() + + conn, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + wg.Add(1) + go func() { + defer wg.Done() + if _, err := pty.Write([]byte(data)); err != nil { + t.Error(err) + } + <-ch + if err := pty.Close(); err != nil { + t.Error(err) + } + }() + + lr := io.LimitReader(tty, int64(len(data))) + if _, err := io.Copy(conn, lr); err != nil { + t.Error(err) + } +} diff --git a/src/net/sockopt_posix.go b/src/net/sockopt_posix.go index a380c7719b484c..2452f06b0a6e32 100644 --- a/src/net/sockopt_posix.go +++ b/src/net/sockopt_posix.go @@ -7,7 +7,6 @@ package net import ( - "internal/bytealg" "runtime" "syscall" ) @@ -43,35 +42,6 @@ func interfaceToIPv4Addr(ifi *Interface) (IP, error) { return nil, errNoSuchInterface } -func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error { - if ifi == nil { - return nil - } - ifat, err := ifi.Addrs() - if err != nil { - return err - } - for _, ifa := range ifat { - switch v := ifa.(type) { - case *IPAddr: - if a := v.IP.To4(); a != nil { - copy(mreq.Interface[:], a) - goto done - } - case *IPNet: - if a := v.IP.To4(); a != nil { - copy(mreq.Interface[:], a) - goto done - } - } - } -done: - if bytealg.Equal(mreq.Multiaddr[:], IPv4zero.To4()) { - return errNoSuchMulticastInterface - } - return nil -} - func setReadBuffer(fd *netFD, bytes int) error { err := fd.pfd.SetsockoptInt(syscall.SOL_SOCKET, syscall.SO_RCVBUF, bytes) runtime.KeepAlive(fd) diff --git a/src/net/sockoptip_bsdvar.go b/src/net/sockoptip4_bsdvar.go similarity index 100% rename from src/net/sockoptip_bsdvar.go rename to src/net/sockoptip4_bsdvar.go diff --git a/src/net/sockoptip_linux.go b/src/net/sockoptip4_linux.go similarity index 68% rename from src/net/sockoptip_linux.go rename to src/net/sockoptip4_linux.go index bd7d8344258e24..8b953ebdc6b438 100644 --- a/src/net/sockoptip_linux.go +++ b/src/net/sockoptip4_linux.go @@ -9,6 +9,16 @@ import ( "syscall" ) +func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { + mreq := &syscall.IPMreqn{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}} + if ifi != nil { + mreq.Ifindex = int32(ifi.Index) + } + err := fd.pfd.SetsockoptIPMreqn(syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) +} + func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { var v int32 if ifi != nil { diff --git a/src/net/sockoptip4_posix_nonlinux.go b/src/net/sockoptip4_posix_nonlinux.go new file mode 100644 index 00000000000000..85e8c6dcfe4116 --- /dev/null +++ b/src/net/sockoptip4_posix_nonlinux.go @@ -0,0 +1,52 @@ +// Copyright 2011 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. + +//go:build (unix && !linux) || windows + +package net + +import ( + "internal/bytealg" + "runtime" + "syscall" +) + +func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { + mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}} + if err := setIPv4MreqToInterface(mreq, ifi); err != nil { + return err + } + err := fd.pfd.SetsockoptIPMreq(syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq) + runtime.KeepAlive(fd) + return wrapSyscallError("setsockopt", err) +} + +func setIPv4MreqToInterface(mreq *syscall.IPMreq, ifi *Interface) error { + if ifi == nil { + return nil + } + ifat, err := ifi.Addrs() + if err != nil { + return err + } + for _, ifa := range ifat { + switch v := ifa.(type) { + case *IPAddr: + if a := v.IP.To4(); a != nil { + copy(mreq.Interface[:], a) + goto done + } + case *IPNet: + if a := v.IP.To4(); a != nil { + copy(mreq.Interface[:], a) + goto done + } + } + } +done: + if bytealg.Equal(mreq.Interface[:], IPv4zero.To4()) { + return errNoSuchMulticastInterface + } + return nil +} diff --git a/src/net/sockoptip_windows.go b/src/net/sockoptip4_windows.go similarity index 100% rename from src/net/sockoptip_windows.go rename to src/net/sockoptip4_windows.go diff --git a/src/net/sockoptip_posix.go b/src/net/sockoptip6_posix.go similarity index 74% rename from src/net/sockoptip_posix.go rename to src/net/sockoptip6_posix.go index 572ea455c09586..5bbc609f7b1a4b 100644 --- a/src/net/sockoptip_posix.go +++ b/src/net/sockoptip6_posix.go @@ -11,16 +11,6 @@ import ( "syscall" ) -func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { - mreq := &syscall.IPMreq{Multiaddr: [4]byte{ip[0], ip[1], ip[2], ip[3]}} - if err := setIPv4MreqToInterface(mreq, ifi); err != nil { - return err - } - err := fd.pfd.SetsockoptIPMreq(syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq) - runtime.KeepAlive(fd) - return wrapSyscallError("setsockopt", err) -} - func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { var v int if ifi != nil { diff --git a/src/net/url/url.go b/src/net/url/url.go index 8a8de1c6a8bdb5..3acde9fb0f3334 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -67,8 +67,9 @@ func unhex(c byte) byte { return c - 'a' + 10 case 'A' <= c && c <= 'F': return c - 'A' + 10 + default: + panic("invalid hex character") } - return 0 } type encoding int diff --git a/src/os/dir.go b/src/os/dir.go index 04392193aa6b03..cc3fd602afe10e 100644 --- a/src/os/dir.go +++ b/src/os/dir.go @@ -140,11 +140,11 @@ func ReadDir(name string) ([]DirEntry, error) { // already exists in the destination, CopyFS will return an error // such that errors.Is(err, fs.ErrExist) will be true. // -// Symbolic links in fsys are not supported. A *PathError with Err set -// to ErrInvalid is returned when copying from a symbolic link. -// // Symbolic links in dir are followed. // +// New files added to fsys (including if dir is a subdirectory of fsys) +// while CopyFS is running are not guaranteed to be copied. +// // Copying stops at and returns the first error encountered. func CopyFS(dir string, fsys fs.FS) error { return fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error { @@ -157,35 +157,38 @@ func CopyFS(dir string, fsys fs.FS) error { return err } newPath := joinPath(dir, fpath) - if d.IsDir() { - return MkdirAll(newPath, 0777) - } - // TODO(panjf2000): handle symlinks with the help of fs.ReadLinkFS - // once https://go.dev/issue/49580 is done. - // we also need filepathlite.IsLocal from https://go.dev/cl/564295. - if !d.Type().IsRegular() { + switch d.Type() { + case ModeDir: + return MkdirAll(newPath, 0777) + case ModeSymlink: + target, err := fs.ReadLink(fsys, path) + if err != nil { + return err + } + return Symlink(target, newPath) + case 0: + r, err := fsys.Open(path) + if err != nil { + return err + } + defer r.Close() + info, err := r.Stat() + if err != nil { + return err + } + w, err := OpenFile(newPath, O_CREATE|O_EXCL|O_WRONLY, 0666|info.Mode()&0777) + if err != nil { + return err + } + + if _, err := io.Copy(w, r); err != nil { + w.Close() + return &PathError{Op: "Copy", Path: newPath, Err: err} + } + return w.Close() + default: return &PathError{Op: "CopyFS", Path: path, Err: ErrInvalid} } - - r, err := fsys.Open(path) - if err != nil { - return err - } - defer r.Close() - info, err := r.Stat() - if err != nil { - return err - } - w, err := OpenFile(newPath, O_CREATE|O_EXCL|O_WRONLY, 0666|info.Mode()&0777) - if err != nil { - return err - } - - if _, err := io.Copy(w, r); err != nil { - w.Close() - return &PathError{Op: "Copy", Path: newPath, Err: err} - } - return w.Close() }) } diff --git a/src/os/dir_plan9.go b/src/os/dir_plan9.go index ab5c1efce5b075..9d28bd7dda4a89 100644 --- a/src/os/dir_plan9.go +++ b/src/os/dir_plan9.go @@ -11,12 +11,19 @@ import ( ) func (file *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { - // If this file has no dirinfo, create one. - d := file.dirinfo.Load() - if d == nil { - d = new(dirInfo) - file.dirinfo.Store(d) + var d *dirInfo + for { + d = file.dirinfo.Load() + if d != nil { + break + } + newD := new(dirInfo) + if file.dirinfo.CompareAndSwap(nil, newD) { + d = newD + break + } } + d.mu.Lock() defer d.mu.Unlock() diff --git a/src/os/dir_unix.go b/src/os/dir_unix.go index eadc1660c212ea..6a0135b70b073b 100644 --- a/src/os/dir_unix.go +++ b/src/os/dir_unix.go @@ -46,11 +46,19 @@ func (d *dirInfo) close() { func (f *File) readdir(n int, mode readdirMode) (names []string, dirents []DirEntry, infos []FileInfo, err error) { // If this file has no dirInfo, create one. - d := f.dirinfo.Load() - if d == nil { - d = new(dirInfo) - f.dirinfo.Store(d) + var d *dirInfo + for { + d = f.dirinfo.Load() + if d != nil { + break + } + newD := new(dirInfo) + if f.dirinfo.CompareAndSwap(nil, newD) { + d = newD + break + } } + d.mu.Lock() defer d.mu.Unlock() if d.buf == nil { diff --git a/src/os/exec.go b/src/os/exec.go index 1220761df5ee09..43b33fe9442864 100644 --- a/src/os/exec.go +++ b/src/os/exec.go @@ -17,236 +17,183 @@ import ( // ErrProcessDone indicates a [Process] has finished. var ErrProcessDone = errors.New("os: process already finished") -type processMode uint8 +// processStatus describes the status of a [Process]. +type processStatus uint32 const ( - // modePID means that Process operations such use the raw PID from the - // Pid field. handle is not used. - // - // This may be due to the host not supporting handles, or because - // Process was created as a literal, leaving handle unset. - // - // This must be the zero value so Process literals get modePID. - modePID processMode = iota - - // modeHandle means that Process operations use handle, which is - // initialized with an OS process handle. - // - // Note that Release and Wait will deactivate and eventually close the - // handle, so acquire may fail, indicating the reason. - modeHandle -) - -type processStatus uint64 - -const ( - // PID/handle OK to use. - statusOK processStatus = 0 + // statusOK means that the Process is ready to use. + statusOK processStatus = iota // statusDone indicates that the PID/handle should not be used because // the process is done (has been successfully Wait'd on). - statusDone processStatus = 1 << 62 + statusDone // statusReleased indicates that the PID/handle should not be used // because the process is released. - statusReleased processStatus = 1 << 63 - - processStatusMask = 0x3 << 62 + statusReleased ) // Process stores the information about a process created by [StartProcess]. type Process struct { Pid int - mode processMode - - // State contains the atomic process state. - // - // In modePID, this consists only of the processStatus fields, which - // indicate if the process is done/released. - // - // In modeHandle, the lower bits also contain a reference count for the - // handle field. - // - // The Process itself initially holds 1 persistent reference. Any - // operation that uses the handle with a system call temporarily holds - // an additional transient reference. This prevents the handle from - // being closed prematurely, which could result in the OS allocating a - // different handle with the same value, leading to Process' methods - // operating on the wrong process. + // state contains the atomic process state. // - // Release and Wait both drop the Process' persistent reference, but - // other concurrent references may delay actually closing the handle - // because they hold a transient reference. - // - // Regardless, we want new method calls to immediately treat the handle - // as unavailable after Release or Wait to avoid extending this delay. - // This is achieved by setting either processStatus flag when the - // Process' persistent reference is dropped. The only difference in the - // flags is the reason the handle is unavailable, which affects the - // errors returned by concurrent calls. - state atomic.Uint64 - - // Used only in modePID. + // This consists of the processStatus fields, + // which indicate if the process is done/released. + state atomic.Uint32 + + // Used only when handle is nil sigMu sync.RWMutex // avoid race between wait and signal - // handle is the OS handle for process actions, used only in - // modeHandle. - // - // handle must be accessed only via the handleTransientAcquire method - // (or during closeHandle), not directly! handle is immutable. + // handle, if not nil, is a pointer to a struct + // that holds the OS-specific process handle. + // This pointer is set when Process is created, + // and never changed afterward. + // This is a pointer to a separate memory allocation + // so that we can use runtime.AddCleanup. + handle *processHandle + + // cleanup is used to clean up the process handle. + cleanup runtime.Cleanup +} + +// processHandle holds an operating system handle to a process. +// This is only used on systems that support that concept, +// currently Linux and Windows. +// This maintains a reference count to the handle, +// and closes the handle when the reference drops to zero. +type processHandle struct { + // The actual handle. This field should not be used directly. + // Instead, use the acquire and release methods. // - // On Windows, it is a handle from OpenProcess. - // On Linux, it is a pidfd. - // It is unused on other GOOSes. + // On Windows this is a handle returned by OpenProcess. + // On Linux this is a pidfd. handle uintptr + + // Number of active references. When this drops to zero + // the handle is closed. + refs atomic.Int32 } +// acquire adds a reference and returns the handle. +// The bool result reports whether acquire succeeded; +// it fails if the handle is already closed. +// Every successful call to acquire should be paired with a call to release. +func (ph *processHandle) acquire() (uintptr, bool) { + for { + refs := ph.refs.Load() + if refs < 0 { + panic("internal error: negative process handle reference count") + } + if refs == 0 { + return 0, false + } + if ph.refs.CompareAndSwap(refs, refs+1) { + return ph.handle, true + } + } +} + +// release releases a reference to the handle. +func (ph *processHandle) release() { + for { + refs := ph.refs.Load() + if refs <= 0 { + panic("internal error: too many releases of process handle") + } + if ph.refs.CompareAndSwap(refs, refs-1) { + if refs == 1 { + ph.closeHandle() + } + return + } + } +} + +// newPIDProcess returns a [Process] for the given PID. func newPIDProcess(pid int) *Process { p := &Process{ - Pid: pid, - mode: modePID, + Pid: pid, } - runtime.SetFinalizer(p, (*Process).Release) return p } +// newHandleProcess returns a [Process] with the given PID and handle. func newHandleProcess(pid int, handle uintptr) *Process { + ph := &processHandle{ + handle: handle, + } + + // Start the reference count as 1, + // meaning the reference from the returned Process. + ph.refs.Store(1) + p := &Process{ Pid: pid, - mode: modeHandle, - handle: handle, + handle: ph, } - p.state.Store(1) // 1 persistent reference - runtime.SetFinalizer(p, (*Process).Release) + + p.cleanup = runtime.AddCleanup(p, (*processHandle).release, ph) + return p } +// newDoneProcess returns a [Process] for the given PID +// that is already marked as done. This is used on Unix systems +// if the process is known to not exist. func newDoneProcess(pid int) *Process { p := &Process{ - Pid: pid, - mode: modeHandle, - // N.B Since we set statusDone, handle will never actually be - // used, so its value doesn't matter. + Pid: pid, } - p.state.Store(uint64(statusDone)) // No persistent reference, as there is no handle. - runtime.SetFinalizer(p, (*Process).Release) + p.state.Store(uint32(statusDone)) // No persistent reference, as there is no handle. return p } +// handleTransientAcquire returns the process handle or, +// if the process is not ready, the current status. func (p *Process) handleTransientAcquire() (uintptr, processStatus) { - if p.mode != modeHandle { + if p.handle == nil { panic("handleTransientAcquire called in invalid mode") } - for { - refs := p.state.Load() - if refs&processStatusMask != 0 { - return 0, processStatus(refs & processStatusMask) - } - new := refs + 1 - if !p.state.CompareAndSwap(refs, new) { - continue - } - return p.handle, statusOK + status := processStatus(p.state.Load()) + if status != statusOK { + return 0, status } -} - -func (p *Process) handleTransientRelease() { - if p.mode != modeHandle { - panic("handleTransientRelease called in invalid mode") + h, ok := p.handle.acquire() + if ok { + return h, statusOK } - for { - state := p.state.Load() - refs := state &^ processStatusMask - status := processStatus(state & processStatusMask) - if refs == 0 { - // This should never happen because - // handleTransientRelease is always paired with - // handleTransientAcquire. - panic("release of handle with refcount 0") - } - if refs == 1 && status == statusOK { - // Process holds a persistent reference and always sets - // a status when releasing that reference - // (handlePersistentRelease). Thus something has gone - // wrong if this is the last release but a status has - // not always been set. - panic("final release of handle without processStatus") - } - new := state - 1 - if !p.state.CompareAndSwap(state, new) { - continue - } - if new&^processStatusMask == 0 { - p.closeHandle() - } - return + // This case means that the handle has been closed. + // We always set the status to non-zero before closing the handle. + // If we get here the status must have been set non-zero after + // we just checked it above. + status = processStatus(p.state.Load()) + if status == statusOK { + panic("inconsistent process status") } + return 0, status } -// Drop the Process' persistent reference on the handle, deactivating future -// Wait/Signal calls with the passed reason. -// -// Returns the status prior to this call. If this is not statusOK, then the -// reference was not dropped or status changed. -func (p *Process) handlePersistentRelease(reason processStatus) processStatus { - if p.mode != modeHandle { - panic("handlePersistentRelease called in invalid mode") - } - - for { - refs := p.state.Load() - status := processStatus(refs & processStatusMask) - if status != statusOK { - // Both Release and successful Wait will drop the - // Process' persistent reference on the handle. We - // can't allow concurrent calls to drop the reference - // twice, so we use the status as a guard to ensure the - // reference is dropped exactly once. - return status - } - if refs == 0 { - // This should never happen because dropping the - // persistent reference always sets a status. - panic("release of handle with refcount 0") - } - new := (refs - 1) | uint64(reason) - if !p.state.CompareAndSwap(refs, new) { - continue - } - if new&^processStatusMask == 0 { - p.closeHandle() - } - return status +// handleTransientRelease releases a handle returned by handleTransientAcquire. +func (p *Process) handleTransientRelease() { + if p.handle == nil { + panic("handleTransientRelease called in invalid mode") } + p.handle.release() } +// pidStatus returns the current process status. func (p *Process) pidStatus() processStatus { - if p.mode != modePID { + if p.handle != nil { panic("pidStatus called in invalid mode") } return processStatus(p.state.Load()) } -func (p *Process) pidDeactivate(reason processStatus) { - if p.mode != modePID { - panic("pidDeactivate called in invalid mode") - } - - // Both Release and successful Wait will deactivate the PID. Only one - // of those should win, so nothing left to do here if the compare - // fails. - // - // N.B. This means that results can be inconsistent. e.g., with a - // racing Release and Wait, Wait may successfully wait on the process, - // returning the wait status, while future calls error with "process - // released" rather than "process done". - p.state.CompareAndSwap(0, uint64(reason)) -} - // ProcAttr holds the attributes that will be applied to a new process // started by StartProcess. type ProcAttr struct { @@ -323,23 +270,58 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) // rendering it unusable in the future. // Release only needs to be called if [Process.Wait] is not. func (p *Process) Release() error { - // Note to future authors: the Release API is cursed. - // - // On Unix and Plan 9, Release sets p.Pid = -1. This is the only part of the - // Process API that is not thread-safe, but it can't be changed now. - // - // On Windows, Release does _not_ modify p.Pid. - // - // On Windows, Wait calls Release after successfully waiting to - // proactively clean up resources. - // - // On Unix and Plan 9, Wait also proactively cleans up resources, but - // can not call Release, as Wait does not set p.Pid = -1. - // - // On Unix and Plan 9, calling Release a second time has no effect. - // - // On Windows, calling Release a second time returns EINVAL. - return p.release() + // Unfortunately, for historical reasons, on systems other + // than Windows, Release sets the Pid field to -1. + // This causes the race detector to report a problem + // on concurrent calls to Release, but we can't change it now. + if runtime.GOOS != "windows" { + p.Pid = -1 + } + + oldStatus := p.doRelease(statusReleased) + + // For backward compatibility, on Windows only, + // we return EINVAL on a second call to Release. + if runtime.GOOS == "windows" { + if oldStatus == statusReleased { + return syscall.EINVAL + } + } + + return nil +} + +// doRelease releases a [Process], setting the status to newStatus. +// If the previous status is not statusOK, this does nothing. +// It returns the previous status. +func (p *Process) doRelease(newStatus processStatus) processStatus { + for { + state := p.state.Load() + oldStatus := processStatus(state) + if oldStatus != statusOK { + return oldStatus + } + + if !p.state.CompareAndSwap(state, uint32(newStatus)) { + continue + } + + // We have successfully released the Process. + // If it has a handle, release the reference we + // created in newHandleProcess. + if p.handle != nil { + // No need for more cleanup. + // We must stop the cleanup before calling release; + // otherwise the cleanup might run concurrently + // with the release, which would cause the reference + // counts to be invalid, causing a panic. + p.cleanup.Stop() + + p.handle.release() + } + + return statusOK + } } // Kill causes the [Process] to exit immediately. Kill does not wait until diff --git a/src/os/exec_linux.go b/src/os/exec_linux.go index b47c6cb191986e..aaa022cb96d028 100644 --- a/src/os/exec_linux.go +++ b/src/os/exec_linux.go @@ -8,6 +8,6 @@ import ( "syscall" ) -func (p *Process) closeHandle() { - syscall.Close(int(p.handle)) +func (ph *processHandle) closeHandle() { + syscall.Close(int(ph.handle)) } diff --git a/src/os/exec_nohandle.go b/src/os/exec_nohandle.go index d06f4091c3eb77..0f70d21ccd8607 100644 --- a/src/os/exec_nohandle.go +++ b/src/os/exec_nohandle.go @@ -6,4 +6,6 @@ package os -func (p *Process) closeHandle() {} +func (ph *processHandle) closeHandle() { + panic("internal error: unexpected call to closeHandle") +} diff --git a/src/os/exec_plan9.go b/src/os/exec_plan9.go index bc7a9cdcbc5f74..357b925b360813 100644 --- a/src/os/exec_plan9.go +++ b/src/os/exec_plan9.go @@ -6,7 +6,6 @@ package os import ( "internal/itoa" - "runtime" "syscall" "time" ) @@ -81,7 +80,7 @@ func (p *Process) wait() (ps *ProcessState, err error) { return nil, NewSyscallError("wait", err) } - p.pidDeactivate(statusDone) + p.doRelease(statusDone) ps = &ProcessState{ pid: waitmsg.Pid, status: &waitmsg, @@ -89,17 +88,6 @@ func (p *Process) wait() (ps *ProcessState, err error) { return ps, nil } -func (p *Process) release() error { - p.Pid = -1 - - // Just mark the PID unusable. - p.pidDeactivate(statusReleased) - - // no need for a finalizer anymore - runtime.SetFinalizer(p, nil) - return nil -} - func findProcess(pid int) (p *Process, err error) { // NOOP for Plan 9. return newPIDProcess(pid), nil diff --git a/src/os/exec_unix.go b/src/os/exec_unix.go index 34467ac7a039ad..f9be8dc06878ac 100644 --- a/src/os/exec_unix.go +++ b/src/os/exec_unix.go @@ -8,7 +8,6 @@ package os import ( "errors" - "runtime" "syscall" "time" ) @@ -21,15 +20,12 @@ const ( func (p *Process) wait() (ps *ProcessState, err error) { // Which type of Process do we have? - switch p.mode { - case modeHandle: + if p.handle != nil { // pidfd return p.pidfdWait() - case modePID: + } else { // Regular PID return p.pidWait() - default: - panic("unreachable") } } @@ -53,7 +49,7 @@ func (p *Process) pidWait() (*ProcessState, error) { if ready { // Mark the process done now, before the call to Wait4, // so that Process.pidSignal will not send a signal. - p.pidDeactivate(statusDone) + p.doRelease(statusDone) // Acquire a write lock on sigMu to wait for any // active call to the signal method to complete. p.sigMu.Lock() @@ -70,7 +66,7 @@ func (p *Process) pidWait() (*ProcessState, error) { if err != nil { return nil, NewSyscallError("wait", err) } - p.pidDeactivate(statusDone) + p.doRelease(statusDone) return &ProcessState{ pid: pid1, status: status, @@ -85,15 +81,12 @@ func (p *Process) signal(sig Signal) error { } // Which type of Process do we have? - switch p.mode { - case modeHandle: + if p.handle != nil { // pidfd return p.pidfdSendSignal(s) - case modePID: + } else { // Regular PID return p.pidSignal(s) - default: - panic("unreachable") } } @@ -125,29 +118,6 @@ func convertESRCH(err error) error { return err } -func (p *Process) release() error { - // We clear the Pid field only for API compatibility. On Unix, Release - // has always set Pid to -1. Internally, the implementation relies - // solely on statusReleased to determine that the Process is released. - p.Pid = pidReleased - - switch p.mode { - case modeHandle: - // Drop the Process' reference and mark handle unusable for - // future calls. - // - // Ignore the return value: we don't care if this was a no-op - // racing with Wait, or a double Release. - p.handlePersistentRelease(statusReleased) - case modePID: - // Just mark the PID unusable. - p.pidDeactivate(statusReleased) - } - // no need for a finalizer anymore - runtime.SetFinalizer(p, nil) - return nil -} - func findProcess(pid int) (p *Process, err error) { h, err := pidfdFind(pid) if err == ErrProcessDone { diff --git a/src/os/exec_windows.go b/src/os/exec_windows.go index ab2dae1d718548..68c7064d2dd0e5 100644 --- a/src/os/exec_windows.go +++ b/src/os/exec_windows.go @@ -12,7 +12,7 @@ import ( "time" ) -// Note that Process.mode is always modeHandle because Windows always requires +// Note that Process.handle is never nil because Windows always requires // a handle. A manually-created Process literal is not valid. func (p *Process) wait() (ps *ProcessState, err error) { @@ -44,7 +44,11 @@ func (p *Process) wait() (ps *ProcessState, err error) { if e != nil { return nil, NewSyscallError("GetProcessTimes", e) } - defer p.Release() + + // For compatibility we use statusReleased here rather + // than statusDone. + p.doRelease(statusReleased) + return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil } @@ -73,23 +77,8 @@ func (p *Process) signal(sig Signal) error { return syscall.Errno(syscall.EWINDOWS) } -func (p *Process) release() error { - // Drop the Process' reference and mark handle unusable for - // future calls. - // - // The API on Windows expects EINVAL if Release is called multiple - // times. - if old := p.handlePersistentRelease(statusReleased); old == statusReleased { - return syscall.EINVAL - } - - // no need for a finalizer anymore - runtime.SetFinalizer(p, nil) - return nil -} - -func (p *Process) closeHandle() { - syscall.CloseHandle(syscall.Handle(p.handle)) +func (ph *processHandle) closeHandle() { + syscall.CloseHandle(syscall.Handle(ph.handle)) } func findProcess(pid int) (p *Process, err error) { diff --git a/src/os/export_linux_test.go b/src/os/export_linux_test.go index 12434cb42671ef..4ace32bb5b2c0b 100644 --- a/src/os/export_linux_test.go +++ b/src/os/export_linux_test.go @@ -14,5 +14,5 @@ var ( const StatusDone = statusDone func (p *Process) Status() processStatus { - return processStatus(p.state.Load() & processStatusMask) + return processStatus(p.state.Load()) } diff --git a/src/os/file.go b/src/os/file.go index a5063680f90589..32ff6be7be2538 100644 --- a/src/os/file.go +++ b/src/os/file.go @@ -685,6 +685,23 @@ func (f *File) SyscallConn() (syscall.RawConn, error) { return newRawConn(f) } +// Fd returns the system file descriptor or handle referencing the open file. +// If f is closed, the descriptor becomes invalid. +// If f is garbage collected, a cleanup may close the descriptor, +// making it invalid; see [runtime.AddCleanup] for more information on when +// a cleanup might be run. +// +// Do not close the returned descriptor; that could cause a later +// close of f to close an unrelated descriptor. +// +// On Unix systems this will cause the [File.SetDeadline] +// methods to stop working. +// +// For most uses prefer the f.SyscallConn method. +func (f *File) Fd() uintptr { + return f.fd() +} + // DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir. // // Note that DirFS("/prefix") only guarantees that the Open calls it makes to the @@ -700,12 +717,17 @@ func (f *File) SyscallConn() (syscall.RawConn, error) { // // The directory dir must not be "". // -// The result implements [io/fs.StatFS], [io/fs.ReadFileFS] and -// [io/fs.ReadDirFS]. +// The result implements [io/fs.StatFS], [io/fs.ReadFileFS], [io/fs.ReadDirFS], and +// [io/fs.ReadLinkFS]. func DirFS(dir string) fs.FS { return dirFS(dir) } +var _ fs.StatFS = dirFS("") +var _ fs.ReadFileFS = dirFS("") +var _ fs.ReadDirFS = dirFS("") +var _ fs.ReadLinkFS = dirFS("") + type dirFS string func (dir dirFS) Open(name string) (fs.File, error) { @@ -777,6 +799,28 @@ func (dir dirFS) Stat(name string) (fs.FileInfo, error) { return f, nil } +func (dir dirFS) Lstat(name string) (fs.FileInfo, error) { + fullname, err := dir.join(name) + if err != nil { + return nil, &PathError{Op: "lstat", Path: name, Err: err} + } + f, err := Lstat(fullname) + if err != nil { + // See comment in dirFS.Open. + err.(*PathError).Path = name + return nil, err + } + return f, nil +} + +func (dir dirFS) ReadLink(name string) (string, error) { + fullname, err := dir.join(name) + if err != nil { + return "", &PathError{Op: "readlink", Path: name, Err: err} + } + return Readlink(fullname) +} + // join returns the path for name in dir. func (dir dirFS) join(name string) (string, error) { if dir == "" { diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go index c123fe696191dd..73df3b086dc3d7 100644 --- a/src/os/file_plan9.go +++ b/src/os/file_plan9.go @@ -23,29 +23,23 @@ func fixLongPath(path string) string { // file is the real representation of *File. // The extra level of indirection ensures that no clients of os -// can overwrite this data, which could cause the finalizer +// can overwrite this data, which could cause the cleanup // to close the wrong file descriptor. type file struct { fdmu poll.FDMutex - fd int + sysfd int name string dirinfo atomic.Pointer[dirInfo] // nil unless directory being read appendMode bool // whether file is opened for appending + cleanup runtime.Cleanup // cleanup closes the file when no longer referenced } -// Fd returns the integer Plan 9 file descriptor referencing the open file. -// If f is closed, the file descriptor becomes invalid. -// If f is garbage collected, a finalizer may close the file descriptor, -// making it invalid; see [runtime.SetFinalizer] for more information on when -// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline] -// methods to stop working. -// -// As an alternative, see the f.SyscallConn method. -func (f *File) Fd() uintptr { +// fd is the Plan 9 implementation of Fd. +func (f *File) fd() uintptr { if f == nil { return ^(uintptr(0)) } - return uintptr(f.fd) + return uintptr(f.sysfd) } // NewFile returns a new File with the given file descriptor and @@ -56,8 +50,8 @@ func NewFile(fd uintptr, name string) *File { if fdi < 0 { return nil } - f := &File{&file{fd: fdi, name: name}} - runtime.SetFinalizer(f.file, (*file).close) + f := &File{&file{sysfd: fdi, name: name}} + f.cleanup = runtime.AddCleanup(f, func(f *file) { f.close() }, f.file) return f } @@ -168,8 +162,9 @@ func (file *file) close() error { err := file.decref() - // no need for a finalizer anymore - runtime.SetFinalizer(file, nil) + // There is no need for a cleanup at this point. File must be alive at the point + // where cleanup.stop is called. + file.cleanup.Stop() return err } @@ -178,7 +173,7 @@ func (file *file) close() error { // and writeUnlock methods. func (file *file) destroy() error { var err error - if e := syscall.Close(file.fd); e != nil { + if e := syscall.Close(file.sysfd); e != nil { err = &PathError{Op: "close", Path: file.name, Err: e} } return err @@ -220,7 +215,7 @@ func (f *File) Truncate(size int64) error { } defer f.decref() - if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + if err = syscall.Fwstat(f.sysfd, buf[:n]); err != nil { return &PathError{Op: "truncate", Path: f.name, Err: err} } return nil @@ -252,7 +247,7 @@ func (f *File) chmod(mode FileMode) error { } defer f.decref() - if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + if err = syscall.Fwstat(f.sysfd, buf[:n]); err != nil { return &PathError{Op: "chmod", Path: f.name, Err: err} } return nil @@ -279,7 +274,7 @@ func (f *File) Sync() error { } defer f.decref() - if err = syscall.Fwstat(f.fd, buf[:n]); err != nil { + if err = syscall.Fwstat(f.sysfd, buf[:n]); err != nil { return &PathError{Op: "sync", Path: f.name, Err: err} } return nil @@ -292,7 +287,7 @@ func (f *File) read(b []byte) (n int, err error) { return 0, err } defer f.readUnlock() - n, e := fixCount(syscall.Read(f.fd, b)) + n, e := fixCount(syscall.Read(f.sysfd, b)) if n == 0 && len(b) > 0 && e == nil { return 0, io.EOF } @@ -307,7 +302,7 @@ func (f *File) pread(b []byte, off int64) (n int, err error) { return 0, err } defer f.readUnlock() - n, e := fixCount(syscall.Pread(f.fd, b, off)) + n, e := fixCount(syscall.Pread(f.sysfd, b, off)) if n == 0 && len(b) > 0 && e == nil { return 0, io.EOF } @@ -326,7 +321,7 @@ func (f *File) write(b []byte) (n int, err error) { if len(b) == 0 { return 0, nil } - return fixCount(syscall.Write(f.fd, b)) + return fixCount(syscall.Write(f.sysfd, b)) } // pwrite writes len(b) bytes to the File starting at byte offset off. @@ -341,7 +336,7 @@ func (f *File) pwrite(b []byte, off int64) (n int, err error) { if len(b) == 0 { return 0, nil } - return fixCount(syscall.Pwrite(f.fd, b, off)) + return fixCount(syscall.Pwrite(f.sysfd, b, off)) } // seek sets the offset for the next Read or Write on file to offset, interpreted @@ -356,7 +351,7 @@ func (f *File) seek(offset int64, whence int) (ret int64, err error) { // Free cached dirinfo, so we allocate a new one if we // access this file as a directory again. See #35767 and #37161. f.dirinfo.Store(nil) - return syscall.Seek(f.fd, offset, whence) + return syscall.Seek(f.sysfd, offset, whence) } // Truncate changes the size of the named file. @@ -553,7 +548,7 @@ func (f *File) Chdir() error { return err } defer f.decref() - if e := syscall.Fchdir(f.fd); e != nil { + if e := syscall.Fchdir(f.sysfd); e != nil { return &PathError{Op: "chdir", Path: f.name, Err: e} } return nil diff --git a/src/os/file_test.go b/src/os/file_test.go new file mode 100644 index 00000000000000..f56a34da3e080f --- /dev/null +++ b/src/os/file_test.go @@ -0,0 +1,156 @@ +// Copyright 2023 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. + +package os_test + +import ( + "internal/testenv" + "io/fs" + . "os" + "path/filepath" + "testing" +) + +func TestDirFSReadLink(t *testing.T) { + testenv.MustHaveSymlink(t) + + root := t.TempDir() + subdir := filepath.Join(root, "dir") + if err := Mkdir(subdir, 0o777); err != nil { + t.Fatal(err) + } + links := map[string]string{ + filepath.Join(root, "parent-link"): filepath.Join("..", "foo"), + filepath.Join(root, "sneaky-parent-link"): filepath.Join("dir", "..", "..", "foo"), + filepath.Join(root, "abs-link"): filepath.Join(root, "foo"), + filepath.Join(root, "rel-link"): "foo", + filepath.Join(root, "rel-sub-link"): filepath.Join("dir", "foo"), + filepath.Join(subdir, "parent-link"): filepath.Join("..", "foo"), + } + for newname, oldname := range links { + if err := Symlink(oldname, newname); err != nil { + t.Fatal(err) + } + } + + fsys := DirFS(root) + want := map[string]string{ + "rel-link": "foo", + "rel-sub-link": filepath.Join("dir", "foo"), + "dir/parent-link": filepath.Join("..", "foo"), + "parent-link": filepath.Join("..", "foo"), + "sneaky-parent-link": filepath.Join("dir", "..", "..", "foo"), + "abs-link": filepath.Join(root, "foo"), + } + for name, want := range want { + got, err := fs.ReadLink(fsys, name) + if got != want || err != nil { + t.Errorf("fs.ReadLink(fsys, %q) = %q, %v; want %q, ", name, got, err, want) + } + } +} + +func TestDirFSLstat(t *testing.T) { + testenv.MustHaveSymlink(t) + + root := t.TempDir() + subdir := filepath.Join(root, "dir") + if err := Mkdir(subdir, 0o777); err != nil { + t.Fatal(err) + } + if err := Symlink("dir", filepath.Join(root, "link")); err != nil { + t.Fatal(err) + } + + fsys := DirFS(root) + want := map[string]fs.FileMode{ + "link": fs.ModeSymlink, + "dir": fs.ModeDir, + } + for name, want := range want { + info, err := fs.Lstat(fsys, name) + var got fs.FileMode + if info != nil { + got = info.Mode().Type() + } + if got != want || err != nil { + t.Errorf("fs.Lstat(fsys, %q).Mode().Type() = %v, %v; want %v, ", name, got, err, want) + } + } +} + +func TestDirFSWalkDir(t *testing.T) { + testenv.MustHaveSymlink(t) + + root := t.TempDir() + subdir := filepath.Join(root, "dir") + if err := Mkdir(subdir, 0o777); err != nil { + t.Fatal(err) + } + if err := Symlink("dir", filepath.Join(root, "link")); err != nil { + t.Fatal(err) + } + if err := WriteFile(filepath.Join(root, "dir", "a"), nil, 0o666); err != nil { + t.Fatal(err) + } + fsys := DirFS(root) + + t.Run("SymlinkRoot", func(t *testing.T) { + wantTypes := map[string]fs.FileMode{ + "link": fs.ModeDir, + "link/a": 0, + } + marks := make(map[string]int) + err := fs.WalkDir(fsys, "link", func(path string, entry fs.DirEntry, err error) error { + marks[path]++ + if want, ok := wantTypes[path]; !ok { + t.Errorf("Unexpected path %q in walk", path) + } else if got := entry.Type(); got != want { + t.Errorf("%s entry type = %v; want %v", path, got, want) + } + if err != nil { + t.Errorf("%s: %v", path, err) + } + return nil + }) + if err != nil { + t.Fatal(err) + } + for path := range wantTypes { + if got := marks[path]; got != 1 { + t.Errorf("%s visited %d times; expected 1", path, got) + } + } + }) + + t.Run("SymlinkPresent", func(t *testing.T) { + wantTypes := map[string]fs.FileMode{ + ".": fs.ModeDir, + "dir": fs.ModeDir, + "dir/a": 0, + "link": fs.ModeSymlink, + } + marks := make(map[string]int) + err := fs.WalkDir(fsys, ".", func(path string, entry fs.DirEntry, err error) error { + marks[path]++ + if want, ok := wantTypes[path]; !ok { + t.Errorf("Unexpected path %q in walk", path) + } else if got := entry.Type(); got != want { + t.Errorf("%s entry type = %v; want %v", path, got, want) + } + if err != nil { + t.Errorf("%s: %v", path, err) + } + return nil + }) + if err != nil { + t.Fatal(err) + } + for path := range wantTypes { + if got := marks[path]; got != 1 { + t.Errorf("%s visited %d times; expected 1", path, got) + } + } + }) +} diff --git a/src/os/file_unix.go b/src/os/file_unix.go index b5c0baf3ab7f18..bb99b5279de53b 100644 --- a/src/os/file_unix.go +++ b/src/os/file_unix.go @@ -54,7 +54,7 @@ func rename(oldname, newname string) error { // file is the real representation of *File. // The extra level of indirection ensures that no clients of os -// can overwrite this data, which could cause the finalizer +// can overwrite this data, which could cause the cleanup // to close the wrong file descriptor. type file struct { pfd poll.FD @@ -63,21 +63,11 @@ type file struct { nonblock bool // whether we set nonblocking mode stdoutOrErr bool // whether this is stdout or stderr appendMode bool // whether file is opened for appending + cleanup runtime.Cleanup // cleanup closes the file when no longer referenced } -// Fd returns the integer Unix file descriptor referencing the open file. -// If f is closed, the file descriptor becomes invalid. -// If f is garbage collected, a finalizer may close the file descriptor, -// making it invalid; see [runtime.SetFinalizer] for more information on when -// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline] -// methods to stop working. -// Because file descriptors can be reused, the returned file descriptor may -// only be closed through the [File.Close] method of f, or by its finalizer during -// garbage collection. Otherwise, during garbage collection the finalizer -// may close an unrelated file descriptor with the same (reused) number. -// -// As an alternative, see the f.SyscallConn method. -func (f *File) Fd() uintptr { +// fd is the Unix implementation of Fd. +func (f *File) fd() uintptr { if f == nil { return ^(uintptr(0)) } @@ -240,7 +230,8 @@ func newFile(fd int, name string, kind newFileKind, nonBlocking bool) *File { } } - runtime.SetFinalizer(f.file, (*file).close) + // Close the file when the File is not live. + f.cleanup = runtime.AddCleanup(f, func(f *file) { f.close() }, f.file) return f } @@ -337,8 +328,9 @@ func (file *file) close() error { err = &PathError{Op: "close", Path: file.name, Err: e} } - // no need for a finalizer anymore - runtime.SetFinalizer(file, nil) + // There is no need for a cleanup at this point. File must be alive at the point + // where cleanup.stop is called. + file.cleanup.Stop() return err } diff --git a/src/os/file_windows.go b/src/os/file_windows.go index 2160f1e6ffda70..c209a9f003b39b 100644 --- a/src/os/file_windows.go +++ b/src/os/file_windows.go @@ -22,22 +22,18 @@ const _UTIME_OMIT = -1 // file is the real representation of *File. // The extra level of indirection ensures that no clients of os -// can overwrite this data, which could cause the finalizer +// can overwrite this data, which could cause the cleanup // to close the wrong file descriptor. type file struct { pfd poll.FD name string dirinfo atomic.Pointer[dirInfo] // nil unless directory being read appendMode bool // whether file is opened for appending + cleanup runtime.Cleanup // cleanup closes the file when no longer referenced } -// Fd returns the Windows handle referencing the open file. -// If f is closed, the file descriptor becomes invalid. -// If f is garbage collected, a finalizer may close the file descriptor, -// making it invalid; see [runtime.SetFinalizer] for more information on when -// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline] -// methods to stop working. -func (file *File) Fd() uintptr { +// fd is the Windows implementation of Fd. +func (file *File) fd() uintptr { if file == nil { return uintptr(syscall.InvalidHandle) } @@ -65,7 +61,7 @@ func newFile(h syscall.Handle, name string, kind string) *File { }, name: name, }} - runtime.SetFinalizer(f.file, (*file).close) + f.cleanup = runtime.AddCleanup(f, func(f *file) { f.close() }, f.file) // Ignore initialization errors. // Assume any problems will show up in later I/O. @@ -129,8 +125,9 @@ func (file *file) close() error { err = &PathError{Op: "close", Path: file.name, Err: e} } - // no need for a finalizer anymore - runtime.SetFinalizer(file, nil) + // There is no need for a cleanup at this point. File must be alive at the point + // where cleanup.stop is called. + file.cleanup.Stop() return err } diff --git a/src/os/os_test.go b/src/os/os_test.go index 1e2db94dea28ab..6bb89f58708575 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -3725,13 +3725,9 @@ func TestCopyFSWithSymlinks(t *testing.T) { t.Fatalf("Mkdir: %v", err) } - // TODO(panjf2000): symlinks are currently not supported, and a specific error - // will be returned. Verify that error and skip the subsequent test, - // revisit this once #49580 is closed. - if err := CopyFS(tmpDupDir, fsys); !errors.Is(err, ErrInvalid) { - t.Fatalf("got %v, want ErrInvalid", err) + if err := CopyFS(tmpDupDir, fsys); err != nil { + t.Fatalf("CopyFS: %v", err) } - t.Skip("skip the subsequent test and wait for #49580") forceMFTUpdateOnWindows(t, tmpDupDir) tmpFsys := DirFS(tmpDupDir) diff --git a/src/os/pidfd_linux.go b/src/os/pidfd_linux.go index fe4a743cf8a8e8..5d89c9d39d60b7 100644 --- a/src/os/pidfd_linux.go +++ b/src/os/pidfd_linux.go @@ -66,6 +66,7 @@ func getPidfd(sysAttr *syscall.SysProcAttr, needDup bool) (uintptr, bool) { return uintptr(h), true } +// pidfdFind returns the process handle for pid. func pidfdFind(pid int) (uintptr, error) { if !pidfdWorks() { return 0, syscall.ENOSYS @@ -78,6 +79,8 @@ func pidfdFind(pid int) (uintptr, error) { return h, nil } +// pidfdWait waits for the process to complete, +// and updates the process status to done. func (p *Process) pidfdWait() (*ProcessState, error) { // When pidfd is used, there is no wait/kill race (described in CL 23967) // because the PID recycle issue doesn't exist (IOW, pidfd, unlike PID, @@ -108,9 +111,11 @@ func (p *Process) pidfdWait() (*ProcessState, error) { if err != nil { return nil, NewSyscallError("waitid", err) } - // Release the Process' handle reference, in addition to the reference - // we took above. - p.handlePersistentRelease(statusDone) + + // Update the Process status to statusDone. + // This also releases a reference to the handle. + p.doRelease(statusDone) + return &ProcessState{ pid: int(info.Pid), status: info.WaitStatus(), @@ -118,6 +123,7 @@ func (p *Process) pidfdWait() (*ProcessState, error) { }, nil } +// pidfdSendSignal sends a signal to the process. func (p *Process) pidfdSendSignal(s syscall.Signal) error { handle, status := p.handleTransientAcquire() switch status { @@ -131,10 +137,12 @@ func (p *Process) pidfdSendSignal(s syscall.Signal) error { return convertESRCH(unix.PidFDSendSignal(handle, s)) } +// pidfdWorks returns whether we can use pidfd on this system. func pidfdWorks() bool { return checkPidfdOnce() == nil } +// checkPidfdOnce is used to only check whether pidfd works once. var checkPidfdOnce = sync.OnceValue(checkPidfd) // checkPidfd checks whether all required pidfd-related syscalls work. This diff --git a/src/os/readfrom_linux_test.go b/src/os/readfrom_linux_test.go index cc0322882b1305..d33f9cf9c984f6 100644 --- a/src/os/readfrom_linux_test.go +++ b/src/os/readfrom_linux_test.go @@ -242,13 +242,12 @@ func testSpliceToTTY(t *testing.T, proto string, size int64) { } var ( - copyFileTests = []copyFileTestFunc{newCopyFileRangeTest, newSendfileOverCopyFileRangeTest} - copyFileHooks = []copyFileTestHook{hookCopyFileRange, hookSendFileOverCopyFileRange} + copyFileTests = []copyFileTestFunc{newCopyFileRangeTest} + copyFileHooks = []copyFileTestHook{hookCopyFileRange} ) func testCopyFiles(t *testing.T, size, limit int64) { testCopyFileRange(t, size, limit) - testSendfileOverCopyFileRange(t, size, limit) } func testCopyFileRange(t *testing.T, size int64, limit int64) { @@ -256,11 +255,6 @@ func testCopyFileRange(t *testing.T, size int64, limit int64) { testCopyFile(t, dst, src, data, hook, limit, name) } -func testSendfileOverCopyFileRange(t *testing.T, size int64, limit int64) { - dst, src, data, hook, name := newSendfileOverCopyFileRangeTest(t, size) - testCopyFile(t, dst, src, data, hook, limit, name) -} - // newCopyFileRangeTest initializes a new test for copy_file_range. // // It hooks package os' call to poll.CopyFileRange and returns the hook, @@ -276,20 +270,6 @@ func newCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte return } -// newSendfileOverCopyFileRangeTest initializes a new test for sendfile over copy_file_range. -// It hooks package os' call to poll.SendFile and returns the hook, -// so it can be inspected. -func newSendfileOverCopyFileRangeTest(t *testing.T, size int64) (dst, src *File, data []byte, hook *copyFileHook, name string) { - t.Helper() - - name = "newSendfileOverCopyFileRangeTest" - - dst, src, data = newCopyFileTest(t, size) - hook, _ = hookSendFileOverCopyFileRange(t) - - return -} - // newSpliceFileTest initializes a new test for splice. // // It creates source sockets and destination file, and populates the source sockets @@ -342,34 +322,6 @@ func hookCopyFileRange(t *testing.T) (hook *copyFileHook, name string) { return } -func hookSendFileOverCopyFileRange(t *testing.T) (*copyFileHook, string) { - return hookSendFileTB(t), "hookSendFileOverCopyFileRange" -} - -func hookSendFileTB(tb testing.TB) *copyFileHook { - // Disable poll.CopyFileRange to force the fallback to poll.SendFile. - originalCopyFileRange := *PollCopyFileRangeP - *PollCopyFileRangeP = func(dst, src *poll.FD, remain int64) (written int64, handled bool, err error) { - return 0, false, nil - } - - hook := new(copyFileHook) - orig := poll.TestHookDidSendFile - tb.Cleanup(func() { - *PollCopyFileRangeP = originalCopyFileRange - poll.TestHookDidSendFile = orig - }) - poll.TestHookDidSendFile = func(dstFD *poll.FD, src int, written int64, err error, handled bool) { - hook.called = true - hook.dstfd = dstFD.Sysfd - hook.srcfd = src - hook.written = written - hook.err = err - hook.handled = handled - } - return hook -} - func hookSpliceFile(t *testing.T) *spliceFileHook { h := new(spliceFileHook) h.install() diff --git a/src/os/readfrom_sendfile_test.go b/src/os/readfrom_sendfile_test.go index dbe1603bd15a49..86ef71ee0293c2 100644 --- a/src/os/readfrom_sendfile_test.go +++ b/src/os/readfrom_sendfile_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build linux || solaris +//go:build solaris package os_test diff --git a/src/os/root.go b/src/os/root.go index 04741c02810baa..fd3b603ed83805 100644 --- a/src/os/root.go +++ b/src/os/root.go @@ -54,13 +54,18 @@ func OpenInRoot(dir, name string) (*File, error) { // // - When GOOS=windows, file names may not reference Windows reserved device names // such as NUL and COM1. +// - On Unix, [Root.Chmod] and [Root.Chown] are vulnerable to a race condition. +// If the target of the operation is changed from a regular file to a symlink +// while the operation is in progress, the operation may be peformed on the link +// rather than the link target. // - When GOOS=js, Root is vulnerable to TOCTOU (time-of-check-time-of-use) // attacks in symlink validation, and cannot ensure that operations will not // escape the root. // - When GOOS=plan9 or GOOS=js, Root does not track directories across renames. // On these platforms, a Root references a directory name, not a file descriptor. +// - WASI preview 1 (GOOS=wasip1) does not support [Root.Chmod]. type Root struct { - root root + root *root } const ( @@ -127,6 +132,12 @@ func (r *Root) OpenRoot(name string) (*Root, error) { return openRootInRoot(r, name) } +// Chmod changes the mode of the named file in the root to mode. +// See [Chmod] for more details. +func (r *Root) Chmod(name string, mode FileMode) error { + return rootChmod(r, name, mode) +} + // Mkdir creates a new directory in the root // with the specified name and permission bits (before umask). // See [Mkdir] for more details. @@ -140,6 +151,12 @@ func (r *Root) Mkdir(name string, perm FileMode) error { return rootMkdir(r, name, perm) } +// Chown changes the numeric uid and gid of the named file in the root. +// See [Chown] for more details. +func (r *Root) Chown(name string, uid, gid int) error { + return rootChown(r, name, uid, gid) +} + // Remove removes the named file or (empty) directory in the root. // See [Remove] for more details. func (r *Root) Remove(name string) error { diff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go index 8d5ead32b9cd0a..919e78c777825d 100644 --- a/src/os/root_noopenat.go +++ b/src/os/root_noopenat.go @@ -49,7 +49,7 @@ func newRoot(name string) (*Root, error) { if !fi.IsDir() { return nil, errors.New("not a directory") } - return &Root{root{name: name}}, nil + return &Root{&root{name: name}}, nil } func (r *root) Close() error { @@ -95,6 +95,26 @@ func rootStat(r *Root, name string, lstat bool) (FileInfo, error) { return fi, nil } +func rootChmod(r *Root, name string, mode FileMode) error { + if err := checkPathEscapes(r, name); err != nil { + return &PathError{Op: "chmodat", Path: name, Err: err} + } + if err := Chmod(joinPath(r.root.name, name), mode); err != nil { + return &PathError{Op: "chmodat", Path: name, Err: underlyingError(err)} + } + return nil +} + +func rootChown(r *Root, name string, uid, gid int) error { + if err := checkPathEscapes(r, name); err != nil { + return &PathError{Op: "chownat", Path: name, Err: err} + } + if err := Chown(joinPath(r.root.name, name), uid, gid); err != nil { + return &PathError{Op: "chownat", Path: name, Err: underlyingError(err)} + } + return nil +} + func rootMkdir(r *Root, name string, perm FileMode) error { if err := checkPathEscapes(r, name); err != nil { return &PathError{Op: "mkdirat", Path: name, Err: err} diff --git a/src/os/root_openat.go b/src/os/root_openat.go index a03208b4c170e9..cac0b1df0faf8b 100644 --- a/src/os/root_openat.go +++ b/src/os/root_openat.go @@ -21,10 +21,11 @@ type root struct { // refs is incremented while an operation is using fd. // closed is set when Close is called. // fd is closed when closed is true and refs is 0. - mu sync.Mutex - fd sysfdType - refs int // number of active operations - closed bool // set when closed + mu sync.Mutex + fd sysfdType + refs int // number of active operations + closed bool // set when closed + cleanup runtime.Cleanup // cleanup closes the file when no longer referenced } func (r *root) Close() error { @@ -34,7 +35,9 @@ func (r *root) Close() error { syscall.Close(r.fd) } r.closed = true - runtime.SetFinalizer(r, nil) // no need for a finalizer any more + // There is no need for a cleanup at this point. Root must be alive at the point + // where cleanup.stop is called. + r.cleanup.Stop() return nil } @@ -64,6 +67,26 @@ func (r *root) Name() string { return r.name } +func rootChmod(r *Root, name string, mode FileMode) error { + _, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) { + return struct{}{}, chmodat(parent, name, mode) + }) + if err != nil { + return &PathError{Op: "chmodat", Path: name, Err: err} + } + return nil +} + +func rootChown(r *Root, name string, uid, gid int) error { + _, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) { + return struct{}{}, chownat(parent, name, uid, gid) + }) + if err != nil { + return &PathError{Op: "chownat", Path: name, Err: err} + } + return nil +} + func rootMkdir(r *Root, name string, perm FileMode) error { _, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) { return struct{}{}, mkdirat(parent, name, perm) @@ -71,7 +94,7 @@ func rootMkdir(r *Root, name string, perm FileMode) error { if err != nil { return &PathError{Op: "mkdirat", Path: name, Err: err} } - return err + return nil } func rootRemove(r *Root, name string) error { @@ -81,7 +104,7 @@ func rootRemove(r *Root, name string) error { if err != nil { return &PathError{Op: "removeat", Path: name, Err: err} } - return err + return nil } // doInRoot performs an operation on a path in a Root. diff --git a/src/os/root_test.go b/src/os/root_test.go index b461ee220804f9..3591214ffd7043 100644 --- a/src/os/root_test.go +++ b/src/os/root_test.go @@ -389,6 +389,43 @@ func TestRootCreate(t *testing.T) { } } +func TestRootChmod(t *testing.T) { + if runtime.GOOS == "wasip1" { + t.Skip("Chmod not supported on " + runtime.GOOS) + } + for _, test := range rootTestCases { + test.run(t, func(t *testing.T, target string, root *os.Root) { + if target != "" { + // Create a file with no read/write permissions, + // to ensure we can use Chmod on an inaccessible file. + if err := os.WriteFile(target, nil, 0o000); err != nil { + t.Fatal(err) + } + } + if runtime.GOOS == "windows" { + // On Windows, Chmod("symlink") affects the link, not its target. + // See issue 71492. + fi, err := root.Lstat(test.open) + if err == nil && !fi.Mode().IsRegular() { + t.Skip("https://go.dev/issue/71492") + } + } + want := os.FileMode(0o666) + err := root.Chmod(test.open, want) + if errEndsTest(t, err, test.wantError, "root.Chmod(%q)", test.open) { + return + } + st, err := os.Stat(target) + if err != nil { + t.Fatalf("os.Stat(%q) = %v", target, err) + } + if got := st.Mode(); got != want { + t.Errorf("after root.Chmod(%q, %v): file mode = %v, want %v", test.open, want, got, want) + } + }) + } +} + func TestRootMkdir(t *testing.T) { for _, test := range rootTestCases { test.run(t, func(t *testing.T, target string, root *os.Root) { @@ -877,6 +914,35 @@ func TestRootConsistencyCreate(t *testing.T) { } } +func TestRootConsistencyChmod(t *testing.T) { + if runtime.GOOS == "wasip1" { + t.Skip("Chmod not supported on " + runtime.GOOS) + } + for _, test := range rootConsistencyTestCases { + test.run(t, func(t *testing.T, path string, r *os.Root) (string, error) { + chmod := os.Chmod + lstat := os.Lstat + if r != nil { + chmod = r.Chmod + lstat = r.Lstat + } + + var m1, m2 os.FileMode + err := chmod(path, 0o555) + fi, err := lstat(path) + if err == nil { + m1 = fi.Mode() + } + err = chmod(path, 0o777) + fi, err = lstat(path) + if err == nil { + m2 = fi.Mode() + } + return fmt.Sprintf("%v %v", m1, m2), err + }) + } +} + func TestRootConsistencyMkdir(t *testing.T) { for _, test := range rootConsistencyTestCases { test.run(t, func(t *testing.T, path string, r *os.Root) (string, error) { @@ -1077,6 +1143,10 @@ func TestRootConcurrentClose(t *testing.T) { first = false } f.Close() + if runtime.GOARCH == "wasm" { + // TODO(go.dev/issue/71134) can lead to goroutine starvation. + runtime.Gosched() + } } }() if err := <-ch; err != nil { diff --git a/src/os/root_unix.go b/src/os/root_unix.go index 4b52b81de71e81..76d6b74eb7364b 100644 --- a/src/os/root_unix.go +++ b/src/os/root_unix.go @@ -48,11 +48,11 @@ func newRoot(fd int, name string) (*Root, error) { syscall.CloseOnExec(fd) } - r := &Root{root{ + r := &Root{&root{ fd: fd, name: name, }} - runtime.SetFinalizer(&r.root, (*root).Close) + r.root.cleanup = runtime.AddCleanup(r, func(f *root) { f.Close() }, r.root) return r, nil } @@ -131,6 +131,40 @@ func rootStat(r *Root, name string, lstat bool) (FileInfo, error) { return fi, nil } +// On systems which use fchmodat, fchownat, etc., we have a race condition: +// When "name" is a symlink, Root.Chmod("name") should act on the target of that link. +// However, fchmodat doesn't allow us to chmod a file only if it is not a symlink; +// the AT_SYMLINK_NOFOLLOW parameter causes the operation to act on the symlink itself. +// +// We do the best we can by first checking to see if the target of the operation is a symlink, +// and only attempting the fchmodat if it is not. If the target is replaced between the check +// and the fchmodat, we will chmod the symlink rather than following it. +// +// This race condition is unfortunate, but does not permit escaping a root: +// We may act on the wrong file, but that file will be contained within the root. +func afterResolvingSymlink(parent int, name string, f func() error) error { + if err := checkSymlink(parent, name, nil); err != nil { + return err + } + return f() +} + +func chmodat(parent int, name string, mode FileMode) error { + return afterResolvingSymlink(parent, name, func() error { + return ignoringEINTR(func() error { + return unix.Fchmodat(parent, name, syscallMode(mode), unix.AT_SYMLINK_NOFOLLOW) + }) + }) +} + +func chownat(parent int, name string, uid, gid int) error { + return afterResolvingSymlink(parent, name, func() error { + return ignoringEINTR(func() error { + return unix.Fchownat(parent, name, uid, gid, unix.AT_SYMLINK_NOFOLLOW) + }) + }) +} + func mkdirat(fd int, name string, perm FileMode) error { return ignoringEINTR(func() error { return unix.Mkdirat(fd, name, syscallMode(perm)) diff --git a/src/os/root_unix_test.go b/src/os/root_unix_test.go new file mode 100644 index 00000000000000..280efc68751ed3 --- /dev/null +++ b/src/os/root_unix_test.go @@ -0,0 +1,87 @@ +// Copyright 2025 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. + +//go:build unix || (js && wasm) || wasip1 + +package os_test + +import ( + "fmt" + "os" + "runtime" + "syscall" + "testing" +) + +func TestRootChown(t *testing.T) { + if runtime.GOOS == "wasip1" { + t.Skip("Chown not supported on " + runtime.GOOS) + } + + // Look up the current default uid/gid. + f := newFile(t) + dir, err := f.Stat() + if err != nil { + t.Fatal(err) + } + sys := dir.Sys().(*syscall.Stat_t) + + groups, err := os.Getgroups() + if err != nil { + t.Fatalf("getgroups: %v", err) + } + groups = append(groups, os.Getgid()) + for _, test := range rootTestCases { + test.run(t, func(t *testing.T, target string, root *os.Root) { + if target != "" { + if err := os.WriteFile(target, nil, 0o666); err != nil { + t.Fatal(err) + } + } + for _, gid := range groups { + err := root.Chown(test.open, -1, gid) + if errEndsTest(t, err, test.wantError, "root.Chown(%q, -1, %v)", test.open, gid) { + return + } + checkUidGid(t, target, int(sys.Uid), gid) + } + }) + } +} + +func TestRootConsistencyChown(t *testing.T) { + if runtime.GOOS == "wasip1" { + t.Skip("Chown not supported on " + runtime.GOOS) + } + groups, err := os.Getgroups() + if err != nil { + t.Fatalf("getgroups: %v", err) + } + var gid int + if len(groups) == 0 { + gid = os.Getgid() + } else { + gid = groups[0] + } + for _, test := range rootConsistencyTestCases { + test.run(t, func(t *testing.T, path string, r *os.Root) (string, error) { + chown := os.Chown + lstat := os.Lstat + if r != nil { + chown = r.Chown + lstat = r.Lstat + } + err := chown(path, -1, gid) + if err != nil { + return "", err + } + fi, err := lstat(path) + if err != nil { + return "", err + } + sys := fi.Sys().(*syscall.Stat_t) + return fmt.Sprintf("%v %v", sys.Uid, sys.Gid), nil + }) + } +} diff --git a/src/os/root_windows.go b/src/os/root_windows.go index dcc311cf86f731..4f391cb2a7d9fd 100644 --- a/src/os/root_windows.go +++ b/src/os/root_windows.go @@ -105,11 +105,11 @@ func newRoot(fd syscall.Handle, name string) (*Root, error) { return nil, &PathError{Op: "open", Path: name, Err: errors.New("not a directory")} } - r := &Root{root{ + r := &Root{&root{ fd: fd, name: name, }} - runtime.SetFinalizer(&r.root, (*root).Close) + r.root.cleanup = runtime.AddCleanup(r, func(f *root) { f.Close() }, r.root) return r, nil } @@ -134,7 +134,7 @@ func rootOpenFileNolog(root *Root, name string, flag int, perm FileMode) (*File, } func openat(dirfd syscall.Handle, name string, flag int, perm FileMode) (syscall.Handle, error) { - h, err := windows.Openat(dirfd, name, flag|syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY, syscallMode(perm)) + h, err := windows.Openat(dirfd, name, uint64(flag)|syscall.O_CLOEXEC|windows.O_NOFOLLOW_ANY, syscallMode(perm)) if err == syscall.ELOOP || err == syscall.ENOTDIR { if link, err := readReparseLinkAt(dirfd, name); err == nil { return syscall.InvalidHandle, errSymlink(link) @@ -232,6 +232,54 @@ func rootStat(r *Root, name string, lstat bool) (FileInfo, error) { return fi, nil } +func chmodat(parent syscall.Handle, name string, mode FileMode) error { + // Currently, on Windows os.Chmod("symlink") will act on "symlink", + // not on any file it points to. + // + // This may or may not be the desired behavior: https://go.dev/issue/71492 + // + // For now, be consistent with os.Symlink. + // Passing O_OPEN_REPARSE causes us to open the named file itself, + // not any file that it links to. + // + // If we want to change this in the future, pass O_NOFOLLOW_ANY instead + // and return errSymlink when encountering a symlink: + // + // if err == syscall.ELOOP || err == syscall.ENOTDIR { + // if link, err := readReparseLinkAt(parent, name); err == nil { + // return errSymlink(link) + // } + // } + h, err := windows.Openat(parent, name, syscall.O_CLOEXEC|windows.O_OPEN_REPARSE|windows.O_WRITE_ATTRS, 0) + if err != nil { + return err + } + defer syscall.CloseHandle(h) + + var d syscall.ByHandleFileInformation + if err := syscall.GetFileInformationByHandle(h, &d); err != nil { + return err + } + attrs := d.FileAttributes + + if mode&syscall.S_IWRITE != 0 { + attrs &^= syscall.FILE_ATTRIBUTE_READONLY + } else { + attrs |= syscall.FILE_ATTRIBUTE_READONLY + } + if attrs == d.FileAttributes { + return nil + } + + var fbi windows.FILE_BASIC_INFO + fbi.FileAttributes = attrs + return windows.SetFileInformationByHandle(h, windows.FileBasicInfo, unsafe.Pointer(&fbi), uint32(unsafe.Sizeof(fbi))) +} + +func chownat(parent syscall.Handle, name string, uid, gid int) error { + return syscall.EWINDOWS // matches syscall.Chown +} + func mkdirat(dirfd syscall.Handle, name string, perm FileMode) error { return windows.Mkdirat(dirfd, name, syscallMode(perm)) } diff --git a/src/os/stat_plan9.go b/src/os/stat_plan9.go index a5e9901379aeac..e9fba17e9d1b51 100644 --- a/src/os/stat_plan9.go +++ b/src/os/stat_plan9.go @@ -59,7 +59,7 @@ func dirstat(arg any) (*syscall.Dir, error) { if err := a.incref("fstat"); err != nil { return nil, err } - n, err = syscall.Fstat(a.fd, buf) + n, err = syscall.Fstat(a.sysfd, buf) a.decref() case string: name = a diff --git a/src/os/user/user_test.go b/src/os/user/user_test.go index 31486aed033819..0e06369bf5a5da 100644 --- a/src/os/user/user_test.go +++ b/src/os/user/user_test.go @@ -6,6 +6,7 @@ package user import ( "os" + "slices" "testing" ) @@ -178,16 +179,7 @@ func TestGroupIds(t *testing.T) { if err != nil { t.Fatalf("%+v.GroupIds(): %v", user, err) } - if !containsID(gids, user.Gid) { + if !slices.Contains(gids, user.Gid) { t.Errorf("%+v.GroupIds() = %v; does not contain user GID %s", user, gids, user.Gid) } } - -func containsID(ids []string, id string) bool { - for _, x := range ids { - if x == id { - return true - } - } - return false -} diff --git a/src/os/user/user_windows_test.go b/src/os/user/user_windows_test.go index c71503372e0107..7dca2fc5f94a16 100644 --- a/src/os/user/user_windows_test.go +++ b/src/os/user/user_windows_test.go @@ -14,6 +14,7 @@ import ( "os" "os/exec" "runtime" + "slices" "strconv" "syscall" "testing" @@ -205,7 +206,7 @@ func TestGroupIdsTestUser(t *testing.T) { if err != nil { t.Fatalf("%+v.GroupIds(): %v", user, err) } - if !containsID(gids, user.Gid) { + if !slices.Contains(gids, user.Gid) { t.Errorf("%+v.GroupIds() = %v; does not contain user GID %s", user, gids, user.Gid) } } diff --git a/src/os/zero_copy_linux.go b/src/os/zero_copy_linux.go index 27a0882560ae2a..9d666a3c791117 100644 --- a/src/os/zero_copy_linux.go +++ b/src/os/zero_copy_linux.go @@ -40,17 +40,16 @@ func (f *File) writeTo(w io.Writer) (written int64, handled bool, err error) { } func (f *File) readFrom(r io.Reader) (written int64, handled bool, err error) { - // Neither copy_file_range(2)/sendfile(2) nor splice(2) supports destinations opened with + // Neither copy_file_range(2) nor splice(2) supports destinations opened with // O_APPEND, so don't bother to try zero-copy with these system calls. // // Visit https://man7.org/linux/man-pages/man2/copy_file_range.2.html#ERRORS and - // https://man7.org/linux/man-pages/man2/sendfile.2.html#ERRORS and // https://man7.org/linux/man-pages/man2/splice.2.html#ERRORS for details. if f.appendMode { return 0, false, nil } - written, handled, err = f.copyFile(r) + written, handled, err = f.copyFileRange(r) if handled { return } @@ -87,7 +86,7 @@ func (f *File) spliceToFile(r io.Reader) (written int64, handled bool, err error return written, handled, wrapSyscallError("splice", err) } -func (f *File) copyFile(r io.Reader) (written int64, handled bool, err error) { +func (f *File) copyFileRange(r io.Reader) (written int64, handled bool, err error) { var ( remain int64 lr *io.LimitedReader @@ -116,44 +115,7 @@ func (f *File) copyFile(r io.Reader) (written int64, handled bool, err error) { if lr != nil { lr.N -= written } - - if handled { - return written, handled, wrapSyscallError("copy_file_range", err) - } - - // If fd_in and fd_out refer to the same file and the source and target ranges overlap, - // copy_file_range(2) just returns EINVAL error. poll.CopyFileRange will ignore that - // error and act like it didn't call copy_file_range(2). Then the caller will fall back - // to generic copy, which results in doubling the content in the file. - // By contrast, sendfile(2) allows this kind of overlapping and works like a memmove, - // in this case the file content will remain the same after copying, which is not what we want. - // Thus, we just bail out here and leave it to generic copy when it's a file copying itself. - if f.pfd.Sysfd == src.pfd.Sysfd { - return 0, false, nil - } - - sc, err := src.SyscallConn() - if err != nil { - return - } - - // We can employ sendfile(2) when copy_file_range(2) fails to handle the copy. - // sendfile(2) enabled file-to-file copying since Linux 2.6.33 and Go requires - // Linux 3.2 or later, so we're good to go. - // Check out https://man7.org/linux/man-pages/man2/sendfile.2.html#DESCRIPTION for more details. - rerr := sc.Read(func(fd uintptr) bool { - written, err, handled = poll.SendFile(&f.pfd, int(fd), remain) - return true - }) - if lr != nil { - lr.N -= written - } - - if err == nil { - err = rerr - } - - return written, handled, wrapSyscallError("sendfile", err) + return written, handled, wrapSyscallError("copy_file_range", err) } // getPollFDAndNetwork tries to get the poll.FD and network type from the given interface diff --git a/src/race.bat b/src/race.bat index d395e19f9741dd..206d4126eea648 100644 --- a/src/race.bat +++ b/src/race.bat @@ -9,43 +9,16 @@ setlocal -if exist make.bat goto ok -echo race.bat must be run from go\src -:: cannot exit: would kill parent command interpreter -goto end -:ok - -set GOROOT=%CD%\.. -call .\make.bat --dist-tool >NUL -if errorlevel 1 goto fail -.\cmd\dist\dist.exe env -w -p >env.bat -if errorlevel 1 goto fail -call .\env.bat -del env.bat - -if %GOHOSTARCH% == amd64 goto continue -echo Race detector is only supported on windows/amd64. -goto fail - -:continue -call .\make.bat --no-banner --no-local -if %GOBUILDFAIL%==1 goto end -echo # go install -race std -go install -race std -if errorlevel 1 goto fail - -go tool dist test -race - -if errorlevel 1 goto fail -goto succ - -:fail -set GOBUILDFAIL=1 -echo Fail. -goto end - -:succ -echo All tests passed. - -:end -if x%GOBUILDEXIT%==x1 exit %GOBUILDFAIL% +if not exist make.bat ( + echo race.bat must be run from go\src + exit /b 1 +) + +if not "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( + echo Race detector is only supported on windows/amd64. + exit /b 1 +) + +call .\make.bat --no-banner || exit /b 1 +go install -race std || exit /b 1 +go tool dist test -race || exit /b 1 diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index b2f70c13697735..3d1e410dac6323 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -6322,12 +6322,12 @@ func TestMapOfGCBigKey(t *testing.T) { const n = 100 m := MakeMap(mt) for i := 0; i < n; i++ { - kv := KV{int64(i), int64(i+1)} + kv := KV{int64(i), int64(i + 1)} m.SetMapIndex(ValueOf(kv), ValueOf(kv)) } for i := 0; i < n; i++ { - kv := KV{int64(i), int64(i+1)} + kv := KV{int64(i), int64(i + 1)} elem := m.MapIndex(ValueOf(kv)).Interface().(KV) if elem != kv { t.Errorf("lost m[%v] = %v, want %v", kv, elem, kv) diff --git a/src/reflect/map_noswiss.go b/src/reflect/map_noswiss.go index eb0a52a3902f6f..19696a4f4bd858 100644 --- a/src/reflect/map_noswiss.go +++ b/src/reflect/map_noswiss.go @@ -17,6 +17,14 @@ type mapType struct { abi.OldMapType } +// Pushed from runtime. + +//go:noescape +func mapiterinit(t *abi.Type, m unsafe.Pointer, it *hiter) + +//go:noescape +func mapiternext(it *hiter) + func (t *rtype) Key() Type { if t.Kind() != Map { panic("reflect: Key of non-map type " + t.String()) diff --git a/src/reflect/map_swiss.go b/src/reflect/map_swiss.go index 75dcb117dfd292..2eac51e57dba5d 100644 --- a/src/reflect/map_swiss.go +++ b/src/reflect/map_swiss.go @@ -8,14 +8,16 @@ package reflect import ( "internal/abi" + "internal/race" "internal/runtime/maps" + "internal/runtime/sys" "unsafe" ) // mapType represents a map type. -type mapType struct { - abi.SwissMapType -} +// +// TODO(prattmic): Only used within this file, could be cleaned up. +type mapType = abi.SwissMapType func (t *rtype) Key() Type { if t.Kind() != Map { @@ -176,6 +178,31 @@ func (v Value) MapIndex(key Value) Value { return copyVal(typ, fl, e) } +// Equivalent to runtime.mapIterStart. +// +//go:noinline +func mapIterStart(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { + if race.Enabled && m != nil { + callerpc := sys.GetCallerPC() + race.ReadPC(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapIterStart)) + } + + it.Init(t, m) + it.Next() +} + +// Equivalent to runtime.mapIterNext. +// +//go:noinline +func mapIterNext(it *maps.Iter) { + if race.Enabled { + callerpc := sys.GetCallerPC() + race.ReadPC(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapIterNext)) + } + + it.Next() +} + // MapKeys returns a slice containing all the keys present in the map, // in unspecified order. // It panics if v's Kind is not [Map]. @@ -187,13 +214,17 @@ func (v Value) MapKeys() []Value { fl := v.flag.ro() | flag(keyType.Kind()) - m := v.pointer() + // Escape analysis can't see that the map doesn't escape. It sees an + // escape from maps.IterStart, via assignment into it, even though it + // doesn't escape this function. + mptr := abi.NoEscape(v.pointer()) + m := (*maps.Map)(mptr) mlen := int(0) if m != nil { - mlen = maplen(m) + mlen = maplen(mptr) } var it maps.Iter - mapiterinit(v.typ(), m, &it) + mapIterStart(tt, m, &it) a := make([]Value, mlen) var i int for i = 0; i < len(a); i++ { @@ -205,7 +236,7 @@ func (v Value) MapKeys() []Value { break } a[i] = copyVal(keyType, fl, key) - mapiternext(&it) + mapIterNext(&it) } return a[:i] } @@ -317,12 +348,14 @@ func (iter *MapIter) Next() bool { panic("MapIter.Next called on an iterator that does not have an associated map Value") } if !iter.hiter.Initialized() { - mapiterinit(iter.m.typ(), iter.m.pointer(), &iter.hiter) + t := (*mapType)(unsafe.Pointer(iter.m.typ())) + m := (*maps.Map)(iter.m.pointer()) + mapIterStart(t, m, &iter.hiter) } else { if iter.hiter.Key() == nil { panic("MapIter.Next called on exhausted iterator") } - mapiternext(&iter.hiter) + mapIterNext(&iter.hiter) } return iter.hiter.Key() != nil } diff --git a/src/reflect/type.go b/src/reflect/type.go index 0e41a6db992e1c..b6fc99a9347cd4 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -301,6 +301,8 @@ const ( ) // Ptr is the old name for the [Pointer] kind. +// +//go:fix inline const Ptr = Pointer // uncommonType is present only for defined types or types with methods @@ -1303,6 +1305,11 @@ func TypeOf(i any) Type { return toType(abi.TypeOf(i)) } +// TypeFor returns the [Type] that represents the type argument T. +func TypeFor[T any]() Type { + return toType(abi.TypeFor[T]()) +} + // rtypeOf directly extracts the *rtype of the provided value. func rtypeOf(i any) *abi.Type { return abi.TypeOf(i) @@ -1318,6 +1325,8 @@ var ptrMap sync.Map // map[*rtype]*ptrType // The two functions behave identically. // // Deprecated: Superseded by [PointerTo]. +// +//go:fix inline func PtrTo(t Type) Type { return PointerTo(t) } // PointerTo returns the pointer type with element t. @@ -2850,12 +2859,3 @@ func addTypeBits(bv *bitVector, offset uintptr, t *abi.Type) { } } } - -// TypeFor returns the [Type] that represents the type argument T. -func TypeFor[T any]() Type { - var v T - if t := TypeOf(v); t != nil { - return t // optimize for T being a non-interface kind - } - return TypeOf((*T)(nil)).Elem() // only for an interface kind -} diff --git a/src/reflect/value.go b/src/reflect/value.go index 4ed94addf99a57..ba5b106c18c970 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -3603,12 +3603,6 @@ func mapdelete(t *abi.Type, m unsafe.Pointer, key unsafe.Pointer) //go:noescape func mapdelete_faststr(t *abi.Type, m unsafe.Pointer, key string) -//go:noescape -func mapiterinit(t *abi.Type, m unsafe.Pointer, it *hiter) - -//go:noescape -func mapiternext(it *hiter) - //go:noescape func maplen(m unsafe.Pointer) int diff --git a/src/run.bat b/src/run.bat index 35c8ead8cb2247..b6a101b2ff0fae 100644 --- a/src/run.bat +++ b/src/run.bat @@ -4,39 +4,17 @@ @echo off -if exist ..\bin\go.exe goto ok -echo Must run run.bat from Go src directory after installing cmd/go. -goto fail -:ok +if not exist ..\bin\go.exe ( + echo Must run run.bat from Go src directory after installing cmd/go. + exit /b 1 +) -:: Keep environment variables within this script -:: unless invoked with --no-local. -if x%1==x--no-local goto nolocal -if x%2==x--no-local goto nolocal setlocal -:nolocal - -set GOBUILDFAIL=0 set GOENV=off -..\bin\go tool dist env > env.bat -if errorlevel 1 goto fail +..\bin\go tool dist env > env.bat || exit /b 1 call .\env.bat del env.bat set GOPATH=c:\nonexist-gopath - -if x%1==x--no-rebuild goto norebuild -..\bin\go tool dist test --rebuild -if errorlevel 1 goto fail -goto end - -:norebuild -..\bin\go tool dist test -if errorlevel 1 goto fail -goto end - -:fail -set GOBUILDFAIL=1 - -:end +..\bin\go tool dist test --rebuild %* || exit /b 1 diff --git a/src/runtime/HACKING.md b/src/runtime/HACKING.md index f0c60f3af9e131..141fae9914afd5 100644 --- a/src/runtime/HACKING.md +++ b/src/runtime/HACKING.md @@ -330,3 +330,69 @@ transitive calls) to prevent stack growth. The conversion from pointer to uintptr must appear in the argument list of any call to this function. This directive is used for some low-level system call implementations. + +Execution tracer +================ + +The execution tracer is a way for users to see what their goroutines are doing, +but they're also useful for runtime hacking. + +Using execution traces to debug runtime problems +------------------------------------------------ + +Execution traces contain a wealth of information about what the runtime is +doing. They contain all goroutine scheduling actions, data about time spent in +the scheduler (P running without a G), data about time spent in the garbage +collector, and more. Use `go tool trace` or [gotraceui](https://gotraceui.dev) +to inspect traces. + +Traces are especially useful for debugging latency issues, and especially if you +can catch the problem in the act. Consider using the flight recorder to help +with this. + +Turn on CPU profiling when you take a trace. This will put the CPU profiling +samples as timestamped events into the trace, allowing you to see execution with +greater detail. If you see CPU profiling sample events appear at a rate that does +not match the sample rate, consider that the OS or platform might be taking away +CPU time from the process, and that you might not be debugging a Go issue. + +If you're really stuck on a problem, adding new instrumentation with the tracer +might help, especially if it's helpful to see events in relation to other +scheduling events. See the next section on modifying the execution tracer. +However, consider using `debuglog` for additional instrumentation first, as that +is far easier to get started with. + +Notes on modifying the execution tracer +--------------------------------------- + +The execution tracer lives in the files whose names start with "trace." +The parser for the execution trace format lives in the `internal/trace` package. + +If you plan on adding new trace events, consider starting with a [trace +experiment](../internal/trace/tracev2/EXPERIMENTS.md). + +If you plan to add new trace instrumentation to the runtime, wrap whatever operation +you're tracing in `traceAcquire` and `traceRelease` fully. These functions mark a +critical section that appears atomic to the execution tracer (but nothing else). + +debuglog +======== + +`debuglog` is a powerful runtime-only debugging tool. Think of it as an +ultra-low-overhead `println` that works just about anywhere in the runtime. +These properties are invaluable when debugging subtle problems in tricky parts +of the codebase. `println` can often perturb code enough to stop data races from +happening, while `debuglog` perturbs execution far less. + +`debuglog` accumulates log messages in a ring buffer on each M, and dumps out +the contents, ordering it by timestamp, on certain kinds of crashes. Some messages +might be lost if the ring buffer gets full, in which case consider increasing the +size, or just work with a partial log. + +1. Add `debuglog` instrumentation to the runtime. Don't forget to call `end`! + Example: `dlog().s("hello world").u32(5).end()` +2. By default, `debuglog` only dumps its contents in certain kinds of crashes. + Consider adding more calls to `printDebugLog` if you're not getting any output. +3. Build the program you wish to debug with the `debuglog` build tag. + +`debuglog` is lower level than execution traces, and much easier to set up. diff --git a/src/runtime/arena.go b/src/runtime/arena.go index 0ffc74e8720550..34821491d54d4c 100644 --- a/src/runtime/arena.go +++ b/src/runtime/arena.go @@ -1008,7 +1008,7 @@ func (h *mheap) allocUserArenaChunk() *mspan { // is mapped contiguously. hintList = &h.arenaHints } - v, size := h.sysAlloc(userArenaChunkBytes, hintList, false) + v, size := h.sysAlloc(userArenaChunkBytes, hintList, &mheap_.userArenaArenas) if size%userArenaChunkBytes != 0 { throw("sysAlloc size is not divisible by userArenaChunkBytes") } diff --git a/src/runtime/asm_arm64.s b/src/runtime/asm_arm64.s index 88bfd3ce5c3fa5..64a1880589390d 100644 --- a/src/runtime/asm_arm64.s +++ b/src/runtime/asm_arm64.s @@ -8,11 +8,6 @@ #include "funcdata.h" #include "textflag.h" -#ifdef GOARM64_LSE -DATA no_lse_msg<>+0x00(SB)/64, $"This program can only run on ARM64 processors with LSE support.\n" -GLOBL no_lse_msg<>(SB), RODATA, $64 -#endif - TEXT runtime·rt0_go(SB),NOSPLIT|TOPFRAME,$0 // SP = stack; R0 = argc; R1 = argv @@ -82,21 +77,6 @@ nocgo: BL runtime·wintls(SB) #endif - // Check that CPU we use for execution supports instructions targeted during compile-time. -#ifdef GOARM64_LSE -#ifndef GOOS_openbsd - // Read the ID_AA64ISAR0_EL1 register - MRS ID_AA64ISAR0_EL1, R0 - - // Extract the LSE field (bits [23:20]) - LSR $20, R0, R0 - AND $0xf, R0, R0 - - // LSE support is indicated by a non-zero value - CBZ R0, no_lse -#endif -#endif - MOVW 8(RSP), R0 // copy argc MOVW R0, -8(RSP) MOVD 16(RSP), R0 // copy argv @@ -115,23 +95,6 @@ nocgo: // start this M BL runtime·mstart(SB) - RET - -#ifdef GOARM64_LSE -#ifndef GOOS_openbsd -no_lse: - MOVD $1, R0 // stderr - MOVD R0, 8(RSP) - MOVD $no_lse_msg<>(SB), R1 // message address - MOVD R1, 16(RSP) - MOVD $64, R2 // message length - MOVD R2, 24(RSP) - CALL runtime·write(SB) - CALL runtime·exit(SB) - CALL runtime·abort(SB) - RET -#endif -#endif // Prevent dead-code elimination of debugCallV2 and debugPinnerV1, which are // intended to be called by debuggers. diff --git a/src/runtime/asm_riscv64.h b/src/runtime/asm_riscv64.h index d4deb093a66164..2414b9f067675a 100644 --- a/src/runtime/asm_riscv64.h +++ b/src/runtime/asm_riscv64.h @@ -10,3 +10,12 @@ #define hasZbb #define hasZbs #endif + +#ifdef GORISCV64_rva23u64 +#define hasV +#define hasZba +#define hasZbb +#define hasZbs +#define hasZfa +#define hasZicond +#endif diff --git a/src/runtime/asm_wasm.s b/src/runtime/asm_wasm.s index 016d2d3825ac49..69da583a1d54e9 100644 --- a/src/runtime/asm_wasm.s +++ b/src/runtime/asm_wasm.s @@ -614,3 +614,13 @@ TEXT runtime·pause(SB), NOSPLIT, $0-8 I32Const $1 Set PAUSE RETUNWIND + +// Called if a wasmexport function is called before runtime initialization +TEXT runtime·notInitialized(SB), NOSPLIT, $0 + MOVD $runtime·wasmStack+(m0Stack__size-16-8)(SB), SP + I32Const $0 // entry PC_B + Call runtime·notInitialized1(SB) + Drop + I32Const $0 // entry PC_B + Call runtime·abort(SB) + UNDEF diff --git a/src/runtime/cgo/handle.go b/src/runtime/cgo/handle.go index 59b65da2b6d763..7d6dd9146c30d3 100644 --- a/src/runtime/cgo/handle.go +++ b/src/runtime/cgo/handle.go @@ -61,10 +61,16 @@ import ( // } // // Some C functions accept a void* argument that points to an arbitrary -// data value supplied by the caller. It is not safe to coerce a [cgo.Handle] +// data value supplied by the caller. It is not safe to coerce a Handle // (an integer) to a Go [unsafe.Pointer], but instead we can pass the address // of the cgo.Handle to the void* parameter, as in this variant of the -// previous example: +// previous example. +// +// Note that, as described in the [cmd/cgo] documentation, +// the C code must not keep a copy of the Go pointer that it receives, +// unless the memory is explicitly pinned using [runtime.Pinner]. +// This example is OK because the C function myprint does not keep +// a copy of the pointer. // // package main // diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index 268ddb59c9e4c1..fcf5ef85ce7dbd 100644 --- a/src/runtime/crash_test.go +++ b/src/runtime/crash_test.go @@ -32,8 +32,11 @@ const entrypointVar = "RUNTIME_TEST_ENTRYPOINT" func TestMain(m *testing.M) { switch entrypoint := os.Getenv(entrypointVar); entrypoint { - case "crash": - crash() + case "panic": + crashViaPanic() + panic("unreachable") + case "trap": + crashViaTrap() panic("unreachable") default: log.Fatalf("invalid %s: %q", entrypointVar, entrypoint) @@ -353,6 +356,37 @@ panic: third panic } +func TestReraisedPanic(t *testing.T) { + output := runTestProg(t, "testprog", "ReraisedPanic") + want := `panic: message [recovered, reraised] +` + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +func TestReraisedMiddlePanic(t *testing.T) { + output := runTestProg(t, "testprog", "ReraisedMiddlePanic") + want := `panic: inner [recovered] + panic: middle [recovered, reraised] + panic: outer +` + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +func TestReraisedPanicSandwich(t *testing.T) { + output := runTestProg(t, "testprog", "ReraisedPanicSandwich") + want := `panic: outer [recovered] + panic: inner [recovered] + panic: outer +` + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + func TestGoexitCrash(t *testing.T) { // External linking brings in cgo, causing deadlock detection not working. testenv.MustInternalLink(t, false) @@ -956,7 +990,8 @@ func TestCrashWhileTracing(t *testing.T) { if err != nil { t.Fatalf("could not create trace.NewReader: %v", err) } - var seen, seenSync bool + var seen bool + nSync := 0 i := 1 loop: for ; ; i++ { @@ -971,7 +1006,7 @@ loop: } switch ev.Kind() { case traceparse.EventSync: - seenSync = true + nSync = ev.Sync().N case traceparse.EventLog: v := ev.Log() if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" { @@ -985,7 +1020,7 @@ loop: if err := cmd.Wait(); err == nil { t.Error("the process should have panicked") } - if !seenSync { + if nSync <= 1 { t.Errorf("expected at least one full generation to have been emitted before the trace was considered broken") } if !seen { diff --git a/src/runtime/debug/mod.go b/src/runtime/debug/mod.go index 53bbf1d8470568..3eab08744f5c29 100644 --- a/src/runtime/debug/mod.go +++ b/src/runtime/debug/mod.go @@ -77,6 +77,7 @@ type Module struct { // - CGO_CPPFLAGS: the effective CGO_CPPFLAGS environment variable // - CGO_CXXFLAGS: the effective CGO_CXXFLAGS environment variable // - CGO_LDFLAGS: the effective CGO_LDFLAGS environment variable +// - DefaultGODEBUG: the effective GODEBUG settings // - GOARCH: the architecture target // - GOAMD64/GOARM/GO386/etc: the architecture feature level for GOARCH // - GOOS: the operating system target diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index 35cb634936a91e..00280ed1b53cab 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -834,7 +834,11 @@ func TestWeakToStrongMarkTermination(t *testing.T) { done <- struct{}{} }() go func() { - time.Sleep(100 * time.Millisecond) + // Usleep here instead of time.Sleep. time.Sleep + // can allocate, and if we get unlucky, then it + // can end up stuck in gcMarkDone with nothing to + // wake it. + runtime.Usleep(100000) // 100ms // Let mark termination continue. runtime.SetSpinInGCMarkDone(false) diff --git a/src/runtime/linkname_swiss.go b/src/runtime/linkname_swiss.go new file mode 100644 index 00000000000000..1be724477e5a50 --- /dev/null +++ b/src/runtime/linkname_swiss.go @@ -0,0 +1,211 @@ +// Copyright 2025 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. + +//go:build goexperiment.swissmap + +package runtime + +import ( + "internal/abi" + "internal/runtime/maps" + "internal/runtime/sys" + "unsafe" +) + +// Legacy //go:linkname compatibility shims +// +// The functions below are unused by the toolchain, and exist only for +// compatibility with existing //go:linkname use in the ecosystem (and in +// map_noswiss.go for normal use via GOEXPERIMENT=noswissmap). + +// linknameIter is the it argument to mapiterinit and mapiternext. +// +// Callers of mapiterinit allocate their own iter structure, which has the +// layout of the pre-Go 1.24 hiter structure, shown here for posterity: +// +// type hiter struct { +// key unsafe.Pointer +// elem unsafe.Pointer +// t *maptype +// h *hmap +// buckets unsafe.Pointer +// bptr *bmap +// overflow *[]*bmap +// oldoverflow *[]*bmap +// startBucket uintptr +// offset uint8 +// wrapped bool +// B uint8 +// i uint8 +// bucket uintptr +// checkBucket uintptr +// } +// +// Our structure must maintain compatibility with the old structure. This +// means: +// +// - Our structure must be the same size or smaller than hiter. Otherwise we +// may write outside the caller's hiter allocation. +// - Our structure must have the same pointer layout as hiter, so that the GC +// tracks pointers properly. +// +// Based on analysis of the "hall of shame" users of these linknames: +// +// - The key and elem fields must be kept up to date with the current key/elem. +// Some users directly access the key and elem fields rather than calling +// reflect.mapiterkey/reflect.mapiterelem. +// - The t field must be non-nil after mapiterinit. gonum.org/v1/gonum uses +// this to verify the iterator is initialized. +// - github.com/segmentio/encoding and github.com/RomiChan/protobuf check if h +// is non-nil, but the code has no effect. Thus the value of h does not +// matter. See internal/runtime_reflect/map.go. +type linknameIter struct { + // Fields from hiter. + key unsafe.Pointer + elem unsafe.Pointer + typ *abi.SwissMapType + + // The real iterator. + it *maps.Iter +} + +// mapiterinit is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// mapiterinit should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/bytedance/sonic +// - github.com/goccy/go-json +// - github.com/RomiChan/protobuf +// - github.com/segmentio/encoding +// - github.com/ugorji/go/codec +// - github.com/wI2L/jettison +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname mapiterinit +func mapiterinit(t *abi.SwissMapType, m *maps.Map, it *linknameIter) { + if raceenabled && m != nil { + callerpc := sys.GetCallerPC() + racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapiterinit)) + } + + it.typ = t + + it.it = new(maps.Iter) + it.it.Init(t, m) + it.it.Next() + + it.key = it.it.Key() + it.elem = it.it.Elem() +} + +// reflect_mapiterinit is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiterinit should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/modern-go/reflect2 +// - gitee.com/quant1x/gox +// - github.com/v2pro/plz +// - github.com/wI2L/jettison +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiterinit reflect.mapiterinit +func reflect_mapiterinit(t *abi.SwissMapType, m *maps.Map, it *linknameIter) { + mapiterinit(t, m, it) +} + +// mapiternext is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// mapiternext should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/bytedance/sonic +// - github.com/RomiChan/protobuf +// - github.com/segmentio/encoding +// - github.com/ugorji/go/codec +// - gonum.org/v1/gonum +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname mapiternext +func mapiternext(it *linknameIter) { + if raceenabled { + callerpc := sys.GetCallerPC() + racereadpc(unsafe.Pointer(it.it.Map()), callerpc, abi.FuncPCABIInternal(mapiternext)) + } + + it.it.Next() + + it.key = it.it.Key() + it.elem = it.it.Elem() +} + +// reflect_mapiternext is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiternext is for package reflect, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - gitee.com/quant1x/gox +// - github.com/modern-go/reflect2 +// - github.com/goccy/go-json +// - github.com/v2pro/plz +// - github.com/wI2L/jettison +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiternext reflect.mapiternext +func reflect_mapiternext(it *linknameIter) { + mapiternext(it) +} + +// reflect_mapiterkey is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiterkey should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/goccy/go-json +// - gonum.org/v1/gonum +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiterkey reflect.mapiterkey +func reflect_mapiterkey(it *linknameIter) unsafe.Pointer { + return it.it.Key() +} + +// reflect_mapiterelem is a compatibility wrapper for map iterator for users of +// //go:linkname from before Go 1.24. It is not used by Go itself. New users +// should use reflect or the maps package. +// +// reflect_mapiterelem should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/goccy/go-json +// - gonum.org/v1/gonum +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname reflect_mapiterelem reflect.mapiterelem +func reflect_mapiterelem(it *linknameIter) unsafe.Pointer { + return it.it.Elem() +} diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index 73d663f7f59e67..60ea2f5188019c 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -640,14 +640,13 @@ func mallocinit() { // hintList is a list of hint addresses for where to allocate new // heap arenas. It must be non-nil. // -// register indicates whether the heap arena should be registered -// in allArenas. -// // sysAlloc returns a memory region in the Reserved state. This region must // be transitioned to Prepared and then Ready before use. // +// arenaList is the list the arena should be added to. +// // h must be locked. -func (h *mheap) sysAlloc(n uintptr, hintList **arenaHint, register bool) (v unsafe.Pointer, size uintptr) { +func (h *mheap) sysAlloc(n uintptr, hintList **arenaHint, arenaList *[]arenaIdx) (v unsafe.Pointer, size uintptr) { assertLockHeld(&h.lock) n = alignUp(n, heapArenaBytes) @@ -790,27 +789,25 @@ mapped: } // Register the arena in allArenas if requested. - if register { - if len(h.allArenas) == cap(h.allArenas) { - size := 2 * uintptr(cap(h.allArenas)) * goarch.PtrSize - if size == 0 { - size = physPageSize - } - newArray := (*notInHeap)(persistentalloc(size, goarch.PtrSize, &memstats.gcMiscSys)) - if newArray == nil { - throw("out of memory allocating allArenas") - } - oldSlice := h.allArenas - *(*notInHeapSlice)(unsafe.Pointer(&h.allArenas)) = notInHeapSlice{newArray, len(h.allArenas), int(size / goarch.PtrSize)} - copy(h.allArenas, oldSlice) - // Do not free the old backing array because - // there may be concurrent readers. Since we - // double the array each time, this can lead - // to at most 2x waste. + if len((*arenaList)) == cap((*arenaList)) { + size := 2 * uintptr(cap((*arenaList))) * goarch.PtrSize + if size == 0 { + size = physPageSize } - h.allArenas = h.allArenas[:len(h.allArenas)+1] - h.allArenas[len(h.allArenas)-1] = ri - } + newArray := (*notInHeap)(persistentalloc(size, goarch.PtrSize, &memstats.gcMiscSys)) + if newArray == nil { + throw("out of memory allocating allArenas") + } + oldSlice := (*arenaList) + *(*notInHeapSlice)(unsafe.Pointer(&(*arenaList))) = notInHeapSlice{newArray, len((*arenaList)), int(size / goarch.PtrSize)} + copy((*arenaList), oldSlice) + // Do not free the old backing array because + // there may be concurrent readers. Since we + // double the array each time, this can lead + // to at most 2x waste. + } + (*arenaList) = (*arenaList)[:len((*arenaList))+1] + (*arenaList)[len((*arenaList))-1] = ri // Store atomically just in case an object from the // new heap arena becomes visible before the heap lock diff --git a/src/runtime/map_swiss.go b/src/runtime/map_swiss.go index 75c72b20f5771c..a8fe87257acd28 100644 --- a/src/runtime/map_swiss.go +++ b/src/runtime/map_swiss.go @@ -39,6 +39,16 @@ func makemap64(t *abi.SwissMapType, hint int64, m *maps.Map) *maps.Map { // makemap_small implements Go map creation for make(map[k]v) and // make(map[k]v, hint) when hint is known to be at most abi.SwissMapGroupSlots // at compile time and the map needs to be allocated on the heap. +// +// makemap_small should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/bytedance/sonic +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname makemap_small func makemap_small() *maps.Map { return maps.NewEmptyMap() } @@ -48,6 +58,16 @@ func makemap_small() *maps.Map { // can be created on the stack, m and optionally m.dirPtr may be non-nil. // If m != nil, the map can be created directly in m. // If m.dirPtr != nil, it points to a group usable for a small map. +// +// makemap should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/ugorji/go/codec +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname makemap func makemap(t *abi.SwissMapType, hint int, m *maps.Map) *maps.Map { if hint < 0 { hint = 0 @@ -68,6 +88,15 @@ func makemap(t *abi.SwissMapType, hint int, m *maps.Map) *maps.Map { //go:linkname mapaccess1 func mapaccess1(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer +// mapaccess2 should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/ugorji/go/codec +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname mapaccess2 func mapaccess2(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) (unsafe.Pointer, bool) func mapaccess1_fat(t *abi.SwissMapType, m *maps.Map, key, zero unsafe.Pointer) unsafe.Pointer { @@ -89,9 +118,29 @@ func mapaccess2_fat(t *abi.SwissMapType, m *maps.Map, key, zero unsafe.Pointer) // mapassign is pushed from internal/runtime/maps. We could just call it, but // we want to avoid one layer of call. // +// mapassign should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/bytedance/sonic +// - github.com/RomiChan/protobuf +// - github.com/segmentio/encoding +// - github.com/ugorji/go/codec +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// //go:linkname mapassign func mapassign(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer +// mapdelete should be an internal detail, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/ugorji/go/codec +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// +//go:linkname mapdelete func mapdelete(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) { if raceenabled && m != nil { callerpc := sys.GetCallerPC() @@ -109,24 +158,26 @@ func mapdelete(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) { m.Delete(t, key) } -// mapiterinit initializes the Iter struct used for ranging over maps. -// The Iter struct pointed to by 'it' is allocated on the stack -// by the compilers order pass or on the heap by reflect_mapiterinit. -// Both need to have zeroed hiter since the struct contains pointers. -func mapiterinit(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { +// mapIterStart initializes the Iter struct used for ranging over maps and +// performs the first step of iteration. The Iter struct pointed to by 'it' is +// allocated on the stack by the compilers order pass or on the heap by +// reflect. Both need to have zeroed it since the struct contains pointers. +func mapIterStart(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { if raceenabled && m != nil { callerpc := sys.GetCallerPC() - racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapiterinit)) + racereadpc(unsafe.Pointer(m), callerpc, abi.FuncPCABIInternal(mapIterStart)) } it.Init(t, m) it.Next() } -func mapiternext(it *maps.Iter) { +// mapIterNext performs the next step of iteration. Afterwards, the next +// key/elem are in it.Key()/it.Elem(). +func mapIterNext(it *maps.Iter) { if raceenabled { callerpc := sys.GetCallerPC() - racereadpc(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapiternext)) + racereadpc(unsafe.Pointer(it.Map()), callerpc, abi.FuncPCABIInternal(mapIterNext)) } it.Next() @@ -145,6 +196,19 @@ func mapclear(t *abi.SwissMapType, m *maps.Map) { // Reflect stubs. Called from ../reflect/asm_*.s +// reflect_makemap is for package reflect, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - gitee.com/quant1x/gox +// - github.com/modern-go/reflect2 +// - github.com/goccy/go-json +// - github.com/RomiChan/protobuf +// - github.com/segmentio/encoding +// - github.com/v2pro/plz +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// //go:linkname reflect_makemap reflect.makemap func reflect_makemap(t *abi.SwissMapType, cap int) *maps.Map { // Check invariants and reflects math. @@ -156,6 +220,16 @@ func reflect_makemap(t *abi.SwissMapType, cap int) *maps.Map { return makemap(t, cap, nil) } +// reflect_mapaccess is for package reflect, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - gitee.com/quant1x/gox +// - github.com/modern-go/reflect2 +// - github.com/v2pro/plz +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// //go:linkname reflect_mapaccess reflect.mapaccess func reflect_mapaccess(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer) unsafe.Pointer { elem, ok := mapaccess2(t, m, key) @@ -176,6 +250,14 @@ func reflect_mapaccess_faststr(t *abi.SwissMapType, m *maps.Map, key string) uns return elem } +// reflect_mapassign is for package reflect, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - gitee.com/quant1x/gox +// - github.com/v2pro/plz +// +// Do not remove or change the type signature. +// //go:linkname reflect_mapassign reflect.mapassign0 func reflect_mapassign(t *abi.SwissMapType, m *maps.Map, key unsafe.Pointer, elem unsafe.Pointer) { p := mapassign(t, m, key) @@ -198,26 +280,15 @@ func reflect_mapdelete_faststr(t *abi.SwissMapType, m *maps.Map, key string) { mapdelete_faststr(t, m, key) } -//go:linkname reflect_mapiterinit reflect.mapiterinit -func reflect_mapiterinit(t *abi.SwissMapType, m *maps.Map, it *maps.Iter) { - mapiterinit(t, m, it) -} - -//go:linkname reflect_mapiternext reflect.mapiternext -func reflect_mapiternext(it *maps.Iter) { - mapiternext(it) -} - -//go:linkname reflect_mapiterkey reflect.mapiterkey -func reflect_mapiterkey(it *maps.Iter) unsafe.Pointer { - return it.Key() -} - -//go:linkname reflect_mapiterelem reflect.mapiterelem -func reflect_mapiterelem(it *maps.Iter) unsafe.Pointer { - return it.Elem() -} - +// reflect_maplen is for package reflect, +// but widely used packages access it using linkname. +// Notable members of the hall of shame include: +// - github.com/goccy/go-json +// - github.com/wI2L/jettison +// +// Do not remove or change the type signature. +// See go.dev/issue/67401. +// //go:linkname reflect_maplen reflect.maplen func reflect_maplen(m *maps.Map) int { if m == nil { diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go index e3c092bef90562..c522c44a4ef476 100644 --- a/src/runtime/map_test.go +++ b/src/runtime/map_test.go @@ -674,10 +674,6 @@ func TestIgnoreBogusMapHint(t *testing.T) { var testNonEscapingMapVariable int = 8 func TestNonEscapingMap(t *testing.T) { - if goexperiment.SwissMap { - t.Skip("TODO(go.dev/issue/54766): implement stack allocated maps") - } - n := testing.AllocsPerRun(1000, func() { m := map[int]int{} m[0] = 0 diff --git a/src/runtime/mcheckmark.go b/src/runtime/mcheckmark.go index f5560cf50f1fdc..03d769e7d3e901 100644 --- a/src/runtime/mcheckmark.go +++ b/src/runtime/mcheckmark.go @@ -39,7 +39,7 @@ func startCheckmarks() { assertWorldStopped() // Clear all checkmarks. - for _, ai := range mheap_.allArenas { + clearCheckmarks := func(ai arenaIdx) { arena := mheap_.arenas[ai.l1()][ai.l2()] bitmap := arena.checkmarks @@ -55,6 +55,13 @@ func startCheckmarks() { clear(bitmap.b[:]) } } + for _, ai := range mheap_.heapArenas { + clearCheckmarks(ai) + } + for _, ai := range mheap_.userArenaArenas { + clearCheckmarks(ai) + } + // Enable checkmarking. useCheckmark = true } @@ -88,8 +95,13 @@ func setCheckmark(obj, base, off uintptr, mbits markBits) bool { ai := arenaIndex(obj) arena := mheap_.arenas[ai.l1()][ai.l2()] - arenaWord := (obj / heapArenaBytes / 8) % uintptr(len(arena.checkmarks.b)) - mask := byte(1 << ((obj / heapArenaBytes) % 8)) + if arena == nil { + // Non-heap pointer. + return false + } + wordIdx := (obj - alignDown(obj, heapArenaBytes)) / goarch.PtrSize + arenaWord := wordIdx / 8 + mask := byte(1 << (wordIdx % 8)) bytep := &arena.checkmarks.b[arenaWord] if atomic.Load8(bytep)&mask != 0 { diff --git a/src/runtime/mcleanup.go b/src/runtime/mcleanup.go index 22d40a5e841243..972532d475c0b4 100644 --- a/src/runtime/mcleanup.go +++ b/src/runtime/mcleanup.go @@ -70,19 +70,19 @@ func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup { // The pointer to the object must be valid. if ptr == nil { - throw("runtime.AddCleanup: ptr is nil") + panic("runtime.AddCleanup: ptr is nil") } usptr := uintptr(unsafe.Pointer(ptr)) // Check that arg is not equal to ptr. - // TODO(67535) this does not cover the case where T and *S are the same - // type and ptr and arg are equal. - if unsafe.Pointer(&arg) == unsafe.Pointer(ptr) { - throw("runtime.AddCleanup: ptr is equal to arg, cleanup will never run") + if kind := abi.TypeOf(arg).Kind(); kind == abi.Pointer || kind == abi.UnsafePointer { + if unsafe.Pointer(ptr) == *((*unsafe.Pointer)(unsafe.Pointer(&arg))) { + panic("runtime.AddCleanup: ptr is equal to arg, cleanup will never run") + } } if inUserArenaChunk(usptr) { // Arena-allocated objects are not eligible for cleanup. - throw("runtime.AddCleanup: ptr is arena-allocated") + panic("runtime.AddCleanup: ptr is arena-allocated") } if debug.sbrk != 0 { // debug.sbrk never frees memory, so no cleanup will ever run @@ -105,7 +105,7 @@ func AddCleanup[T, S any](ptr *T, cleanup func(S), arg S) Cleanup { // Cleanup is a noop. return Cleanup{} } - throw("runtime.AddCleanup: ptr not in allocated block") + panic("runtime.AddCleanup: ptr not in allocated block") } // Ensure we have a finalizer processing goroutine running. diff --git a/src/runtime/mcleanup_test.go b/src/runtime/mcleanup_test.go index 8c2d1f06477fdd..d62356feefb049 100644 --- a/src/runtime/mcleanup_test.go +++ b/src/runtime/mcleanup_test.go @@ -269,3 +269,30 @@ func TestCleanupStopAfterCleanupRuns(t *testing.T) { <-ch stop() } + +func TestCleanupPointerEqualsArg(t *testing.T) { + // See go.dev/issue/71316 + defer func() { + want := "runtime.AddCleanup: ptr is equal to arg, cleanup will never run" + if r := recover(); r == nil { + t.Error("want panic, test did not panic") + } else if r == want { + // do nothing + } else { + t.Errorf("wrong panic: want=%q, got=%q", want, r) + } + }() + + // allocate struct with pointer to avoid hitting tinyalloc. + // Otherwise we can't be sure when the allocation will + // be freed. + type T struct { + v int + p unsafe.Pointer + } + v := &new(T).v + *v = 97531 + runtime.AddCleanup(v, func(x *int) {}, v) + v = nil + runtime.GC() +} diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 48001cfdb94631..d10f3c09cf1da0 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -1056,13 +1056,22 @@ func gcMarkTermination(stw worldStop) { // mark using checkmark bits, to check that we // didn't forget to mark anything during the // concurrent mark process. + // + // Turn off gcwaiting because that will force + // gcDrain to return early if this goroutine + // happens to have its preemption flag set. + // This is fine because the world is stopped. + // Restore it after we're done just to be safe. + sched.gcwaiting.Store(false) startCheckmarks() gcResetMarkState() + gcMarkRootPrepare() gcw := &getg().m.p.ptr().gcw gcDrain(gcw, 0) wbBufFlush1(getg().m.p.ptr()) gcw.dispose() endCheckmarks() + sched.gcwaiting.Store(true) } // marking is complete so we can turn the write barrier off @@ -1544,7 +1553,7 @@ func gcBgMarkWorker(ready chan struct{}) { // We'll releasem after this point and thus this P may run // something else. We must clear the worker mode to avoid // attributing the mode to a different (non-worker) G in - // traceGoStart. + // tracev2.GoStart. pp.gcMarkWorkerMode = gcMarkWorkerNotWorker // If this worker reached a background mark completion @@ -1684,7 +1693,7 @@ func gcSweep(mode gcMode) bool { mheap_.sweepgen += 2 sweep.active.reset() mheap_.pagesSwept.Store(0) - mheap_.sweepArenas = mheap_.allArenas + mheap_.sweepArenas = mheap_.heapArenas mheap_.reclaimIndex.Store(0) mheap_.reclaimCredit.Store(0) unlock(&mheap_.lock) @@ -1747,7 +1756,7 @@ func gcResetMarkState() { // Clear page marks. This is just 1MB per 64GB of heap, so the // time here is pretty trivial. lock(&mheap_.lock) - arenas := mheap_.allArenas + arenas := mheap_.heapArenas unlock(&mheap_.lock) for _, ai := range arenas { ha := mheap_.arenas[ai.l1()][ai.l2()] diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 823b2bd7df9a04..92ef215ee094d8 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -89,9 +89,9 @@ func gcMarkRootPrepare() { // // Break up the work into arenas, and further into chunks. // - // Snapshot allArenas as markArenas. This snapshot is safe because allArenas + // Snapshot heapArenas as markArenas. This snapshot is safe because heapArenas // is append-only. - mheap_.markArenas = mheap_.allArenas[:len(mheap_.allArenas):len(mheap_.allArenas)] + mheap_.markArenas = mheap_.heapArenas[:len(mheap_.heapArenas):len(mheap_.heapArenas)] work.nSpanRoots = len(mheap_.markArenas) * (pagesPerArena / pagesPerSpanRoot) // Scan stacks. @@ -1614,13 +1614,13 @@ func greyobject(obj, base, off uintptr, span *mspan, gcw *gcWork, objIndex uintp if arena.pageMarks[pageIdx]&pageMask == 0 { atomic.Or8(&arena.pageMarks[pageIdx], pageMask) } + } - // If this is a noscan object, fast-track it to black - // instead of greying it. - if span.spanclass.noscan() { - gcw.bytesMarked += uint64(span.elemsize) - return - } + // If this is a noscan object, fast-track it to black + // instead of greying it. + if span.spanclass.noscan() { + gcw.bytesMarked += uint64(span.elemsize) + return } // We're adding obj to P's local workbuf, so it's likely diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index e058dd848925a4..21ae5b1a3b5a1b 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -108,9 +108,9 @@ type mheap struct { // Page reclaimer state - // reclaimIndex is the page index in allArenas of next page to + // reclaimIndex is the page index in heapArenas of next page to // reclaim. Specifically, it refers to page (i % - // pagesPerArena) of arena allArenas[i / pagesPerArena]. + // pagesPerArena) of arena heapArenas[i / pagesPerArena]. // // If this is >= 1<<63, the page reclaimer is done scanning // the page marks. @@ -165,22 +165,31 @@ type mheap struct { // (the actual arenas). This is only used on 32-bit. arena linearAlloc - // allArenas is the arenaIndex of every mapped arena. This can - // be used to iterate through the address space. + // heapArenas is the arenaIndex of every mapped arena mapped for the heap. + // This can be used to iterate through the heap address space. // // Access is protected by mheap_.lock. However, since this is // append-only and old backing arrays are never freed, it is // safe to acquire mheap_.lock, copy the slice header, and // then release mheap_.lock. - allArenas []arenaIdx + heapArenas []arenaIdx - // sweepArenas is a snapshot of allArenas taken at the + // userArenaArenas is the arenaIndex of every mapped arena mapped for + // user arenas. + // + // Access is protected by mheap_.lock. However, since this is + // append-only and old backing arrays are never freed, it is + // safe to acquire mheap_.lock, copy the slice header, and + // then release mheap_.lock. + userArenaArenas []arenaIdx + + // sweepArenas is a snapshot of heapArenas taken at the // beginning of the sweep cycle. This can be read safely by // simply blocking GC (by disabling preemption). sweepArenas []arenaIdx - // markArenas is a snapshot of allArenas taken at the beginning - // of the mark cycle. Because allArenas is append-only, neither + // markArenas is a snapshot of heapArenas taken at the beginning + // of the mark cycle. Because heapArenas is append-only, neither // this slice nor its contents will change during the mark, so // it can be read safely. markArenas []arenaIdx @@ -1494,7 +1503,7 @@ func (h *mheap) grow(npage uintptr) (uintptr, bool) { // Not enough room in the current arena. Allocate more // arena space. This may not be contiguous with the // current arena, so we have to request the full ask. - av, asize := h.sysAlloc(ask, &h.arenaHints, true) + av, asize := h.sysAlloc(ask, &h.arenaHints, &h.heapArenas) if av == nil { inUse := gcController.heapFree.load() + gcController.heapReleased.load() + gcController.heapInUse.load() print("runtime: out of memory: cannot allocate ", ask, "-byte block (", inUse, " in use)\n") diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index 08500a90d532ed..6a9cf77a43fcf0 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -370,7 +370,7 @@ func genARM64() { p("MOVD -8(RSP), R29") // restore frame pointer p("MOVD (RSP), R27") // load PC to REGTMP p("ADD $%d, RSP", l.stack+16) // pop frame (including the space pushed by sigctxt.pushCall) - p("JMP (R27)") + p("RET (R27)") } func genMIPS(_64bit bool) { diff --git a/src/runtime/os_linux_riscv64.go b/src/runtime/os_linux_riscv64.go index 9be88a5ad24468..c4a4d4e50d4506 100644 --- a/src/runtime/os_linux_riscv64.go +++ b/src/runtime/os_linux_riscv64.go @@ -4,4 +4,34 @@ package runtime +import ( + "internal/runtime/syscall" + "unsafe" +) + func osArchInit() {} + +type riscvHWProbePairs = struct { + key int64 + value uint64 +} + +// TODO: Consider whether to use the VDSO entry for riscv_hwprobe. +// There is a VDSO entry for riscv_hwprobe that should allow us to avoid the syscall +// entirely as it can handle the case where the caller only requests extensions that are +// supported on all cores, which is what we're doing here. However, as we're only calling +// this syscall once, it may not be worth the added effort to implement the VDSO call. + +//go:linkname internal_cpu_riscvHWProbe internal/cpu.riscvHWProbe +func internal_cpu_riscvHWProbe(pairs []riscvHWProbePairs, flags uint) bool { + // sys_RISCV_HWPROBE is copied from golang.org/x/sys/unix/zsysnum_linux_riscv64.go. + const sys_RISCV_HWPROBE uintptr = 258 + + if len(pairs) == 0 { + return false + } + // Passing in a cpuCount of 0 and a cpu of nil ensures that only extensions supported by all the + // cores are returned, which is the behaviour we want in internal/cpu. + _, _, e1 := syscall.Syscall6(sys_RISCV_HWPROBE, uintptr(unsafe.Pointer(&pairs[0])), uintptr(len(pairs)), uintptr(0), uintptr(unsafe.Pointer(nil)), uintptr(flags), 0) + return e1 == 0 +} diff --git a/src/runtime/panic.go b/src/runtime/panic.go index dc7a7fe357e091..2dd3c3c2dbfe0d 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -649,6 +649,13 @@ func preprintpanics(p *_panic) { } }() for p != nil { + if p.link != nil && *efaceOf(&p.link.arg) == *efaceOf(&p.arg) { + // This panic contains the same value as the next one in the chain. + // Mark it as reraised. We will skip printing it twice in a row. + p.link.reraised = true + p = p.link + continue + } switch v := p.arg.(type) { case error: p.arg = v.Error() @@ -664,6 +671,9 @@ func preprintpanics(p *_panic) { func printpanics(p *_panic) { if p.link != nil { printpanics(p.link) + if p.link.reraised { + return + } if !p.link.goexit { print("\t") } @@ -673,7 +683,9 @@ func printpanics(p *_panic) { } print("panic: ") printpanicval(p.arg) - if p.recovered { + if p.reraised { + print(" [recovered, reraised]") + } else if p.recovered { print(" [recovered]") } print("\n") @@ -1068,9 +1080,6 @@ func internal_sync_fatal(s string) { // throw should be used for runtime-internal fatal errors where Go itself, // rather than user code, may be at fault for the failure. // -// NOTE: temporarily marked "go:noinline" pending investigation/fix of -// issue #67274, so as to fix longtest builders. -// // throw should be an internal detail, // but widely used packages access it using linkname. // Notable members of the hall of shame include: diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go index f6b4a5c367f05f..b7680a13fdcb25 100644 --- a/src/runtime/pprof/pprof.go +++ b/src/runtime/pprof/pprof.go @@ -555,7 +555,7 @@ func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) { if name == "" { show = true fmt.Fprintf(w, "#\t%#x\n", frame.PC) - } else if name != "runtime.goexit" && (show || !strings.HasPrefix(name, "runtime.")) { + } else if name != "runtime.goexit" && (show || !(strings.HasPrefix(name, "runtime.") || strings.HasPrefix(name, "internal/runtime/"))) { // Hide runtime.goexit and any runtime functions at the beginning. // This is useful mainly for allocation traces. show = true diff --git a/src/runtime/pprof/protomem.go b/src/runtime/pprof/protomem.go index ab3550f43f9a39..72aad82b300534 100644 --- a/src/runtime/pprof/protomem.go +++ b/src/runtime/pprof/protomem.go @@ -36,7 +36,7 @@ func writeHeapProto(w io.Writer, p []profilerecord.MemProfileRecord, rate int64, // what appendLocsForStack expects. if hideRuntime { for i, addr := range stk { - if f := runtime.FuncForPC(addr); f != nil && strings.HasPrefix(f.Name(), "runtime.") { + if f := runtime.FuncForPC(addr); f != nil && (strings.HasPrefix(f.Name(), "runtime.") || strings.HasPrefix(f.Name(), "internal/runtime/")) { continue } // Found non-runtime. Show any runtime uses above it. diff --git a/src/runtime/pprof/protomem_test.go b/src/runtime/pprof/protomem_test.go index 885f4dca5b8762..4d08e67ddc7540 100644 --- a/src/runtime/pprof/protomem_test.go +++ b/src/runtime/pprof/protomem_test.go @@ -118,7 +118,7 @@ func locationToStrings(loc *profile.Location, funcs []string) []string { return funcs } -// This is a regression test for https://go.dev/issue/64528 . +// This is a regression test for https://go.dev/issue/64528. func TestGenericsHashKeyInPprofBuilder(t *testing.T) { if asan.Enabled { t.Skip("extra allocations with -asan throw off the test; see #70079") @@ -229,3 +229,61 @@ func TestGenericsInlineLocations(t *testing.T) { t.Errorf("expected a location with at least 3 functions\n%s\ngot\n%s\n", expectedLocation, actual) } } + +func growMap() { + m := make(map[int]int) + for i := range 512 { + m[i] = i + } +} + +// Runtime frames are hidden in heap profiles. +// This is a regression test for https://go.dev/issue/71174. +func TestHeapRuntimeFrames(t *testing.T) { + previousRate := runtime.MemProfileRate + runtime.MemProfileRate = 1 + defer func() { + runtime.MemProfileRate = previousRate + }() + + growMap() + + runtime.GC() + buf := bytes.NewBuffer(nil) + if err := WriteHeapProfile(buf); err != nil { + t.Fatalf("writing profile: %v", err) + } + p, err := profile.Parse(buf) + if err != nil { + t.Fatalf("profile.Parse: %v", err) + } + + actual := profileToStrings(p) + + // We must see growMap at least once. + foundGrowMap := false + for _, l := range actual { + if !strings.Contains(l, "runtime/pprof.growMap") { + continue + } + foundGrowMap = true + + // Runtime frames like mapassign and map internals should be hidden. + if strings.Contains(l, "runtime.") { + t.Errorf("Sample got %s, want no runtime frames", l) + } + if strings.Contains(l, "internal/runtime/") { + t.Errorf("Sample got %s, want no runtime frames", l) + } + if strings.Contains(l, "runtime/internal/") { + t.Errorf("Sample got %s, want no runtime frames", l) + } + if strings.Contains(l, "mapassign") { // in case mapassign moves to a package not matching above paths. + t.Errorf("Sample got %s, want no mapassign frames", l) + } + } + + if !foundGrowMap { + t.Errorf("Profile got:\n%s\nwant sample in runtime/pprof.growMap", strings.Join(actual, "\n")) + } +} diff --git a/src/runtime/pprof/vminfo_darwin_test.go b/src/runtime/pprof/vminfo_darwin_test.go index 4c0a0fefd87600..6d375c5d53368a 100644 --- a/src/runtime/pprof/vminfo_darwin_test.go +++ b/src/runtime/pprof/vminfo_darwin_test.go @@ -97,7 +97,7 @@ func useVMMap(t *testing.T) (hi, lo uint64, retryable bool, err error) { t.Logf("vmmap output: %s", out) if ee, ok := cmdErr.(*exec.ExitError); ok && len(ee.Stderr) > 0 { t.Logf("%v: %v\n%s", cmd, cmdErr, ee.Stderr) - if testing.Short() && strings.Contains(string(ee.Stderr), "No process corpse slots currently available, waiting to get one") { + if testing.Short() && (strings.Contains(string(ee.Stderr), "No process corpse slots currently available, waiting to get one") || strings.Contains(string(ee.Stderr), "Failed to generate corpse from the process")) { t.Skipf("Skipping knwn flake in short test mode") } retryable = bytes.Contains(ee.Stderr, []byte("resource shortage")) diff --git a/src/runtime/preempt_arm64.s b/src/runtime/preempt_arm64.s index c27d475dee6ae0..31ec9d940f76d4 100644 --- a/src/runtime/preempt_arm64.s +++ b/src/runtime/preempt_arm64.s @@ -82,4 +82,4 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0 MOVD -8(RSP), R29 MOVD (RSP), R27 ADD $512, RSP - JMP (R27) + RET (R27) diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 63629602004832..ce6cf88d0cb19f 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -3894,23 +3894,23 @@ func injectglist(glist *gList) { if glist.empty() { return } - trace := traceAcquire() - if trace.ok() { - for gp := glist.head.ptr(); gp != nil; gp = gp.schedlink.ptr() { - trace.GoUnpark(gp, 0) - } - traceRelease(trace) - } // Mark all the goroutines as runnable before we put them // on the run queues. head := glist.head.ptr() var tail *g qsize := 0 + trace := traceAcquire() for gp := head; gp != nil; gp = gp.schedlink.ptr() { tail = gp qsize++ casgstatus(gp, _Gwaiting, _Grunnable) + if trace.ok() { + trace.GoUnpark(gp, 0) + } + } + if trace.ok() { + traceRelease(trace) } // Turn the gList into a gQueue. @@ -4693,7 +4693,7 @@ func exitsyscall() { trace.GoSysExit(lostP) if lostP { // We lost the P at some point, even though we got it back here. - // Trace that we're starting again, because there was a traceGoSysBlock + // Trace that we're starting again, because there was a tracev2.GoSysBlock // call somewhere in exitsyscallfast (indicating that this goroutine // had blocked) and we're about to start running again. trace.GoStart() @@ -4790,7 +4790,7 @@ func exitsyscallfast_reacquired(trace traceLocker) { if gp.m.syscalltick != gp.m.p.ptr().syscalltick { if trace.ok() { // The p was retaken and then enter into syscall again (since gp.m.syscalltick has changed). - // traceGoSysBlock for this syscall was already emitted, + // tracev2.GoSysBlock for this syscall was already emitted, // but here we effectively retake the p from the new syscall running on the same p. systemstack(func() { // We're stealing the P. It's treated diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index e8e614815d236b..3afb6558b03f50 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -290,23 +290,15 @@ func setCrashFD(fd uintptr) uintptr { } // auxv is populated on relevant platforms but defined here for all platforms -// so x/sys/cpu can assume the getAuxv symbol exists without keeping its list -// of auxv-using GOOS build tags in sync. +// so x/sys/cpu and x/sys/unix can assume the getAuxv symbol exists without +// keeping its list of auxv-using GOOS build tags in sync. // // It contains an even number of elements, (tag, value) pairs. var auxv []uintptr -// golang.org/x/sys/cpu uses getAuxv via linkname. +// golang.org/x/sys/cpu and golang.org/x/sys/unix use getAuxv via linkname. // Do not remove or change the type signature. -// (See go.dev/issue/57336.) -// -// getAuxv should be an internal detail, -// but widely used packages access it using linkname. -// Notable members of the hall of shame include: -// - github.com/cilium/ebpf -// -// Do not remove or change the type signature. -// See go.dev/issue/67401. +// See go.dev/issue/57336 and go.dev/issue/67401. // //go:linkname getAuxv func getAuxv() []uintptr { return auxv } diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index b47c589075ff60..fb16f6daefda40 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -440,6 +440,23 @@ func parsedebugvars() { debug.malloc = (debug.inittrace | debug.sbrk) != 0 debug.profstackdepth = min(debug.profstackdepth, maxProfStackDepth) + // Disable async preemption in checkmark mode. The following situation is + // problematic with checkmark mode: + // + // - The GC doesn't mark object A because it is truly dead. + // - The GC stops the world, asynchronously preempting G1 which has a reference + // to A in its top stack frame + // - During the stop the world, we run the second checkmark GC. It marks the roots + // and discovers A through G1. + // - Checkmark mode reports a failure since there's a discrepancy in mark metadata. + // + // We could disable just conservative scanning during the checkmark scan, which is + // safe but makes checkmark slightly less powerful, but that's a lot more invasive + // than just disabling async preemption altogether. + if debug.gccheckmark > 0 { + debug.asyncpreemptoff = 1 + } + setTraceback(gogetenv("GOTRACEBACK")) traceback_env = traceback_cache } diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index e837c28af8715d..7280643f483566 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -1016,6 +1016,7 @@ type _panic struct { slotsPtr unsafe.Pointer recovered bool // whether this panic has been recovered + reraised bool // whether this panic was reraised goexit bool deferreturn bool } diff --git a/src/runtime/semasleep_test.go b/src/runtime/semasleep_test.go index 711d5df7352565..c891bc7e0c4671 100644 --- a/src/runtime/semasleep_test.go +++ b/src/runtime/semasleep_test.go @@ -91,10 +91,31 @@ func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) { // pthread_cond_timedwait_relative_np. ticker := time.NewTicker(200 * time.Millisecond) defer ticker.Stop() + + checkDoneErr := func(err error) { + if err != nil { + t.Fatalf("The program returned but unfortunately with an error: %v", err) + } + if time.Since(beforeStart) < 1*time.Second { + // The program was supposed to sleep for a full (monotonic) second; + // it should not return before that has elapsed. + t.Fatalf("The program stopped too quickly.") + } + } for { select { case now := <-ticker.C: if now.Sub(ready) > timeout { + // If we got paused for a long time, for example if GODEBUG=gcstoptheworld=2, + // it could be that the subprocess did actually finish and not deadlock, we + // just got stuck as runnable or our wakeup was delayed. Double-check that + // we don't have anything from doneCh before declaring failure. + select { + case err := <-doneCh: + checkDoneErr(err) + return + default: + } t.Error("Program failed to return on time and has to be killed, issue #27520 still exists") // Send SIGQUIT to get a goroutine dump. // Stop sending SIGIO so that the program can clean up and actually terminate. @@ -107,14 +128,7 @@ func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) { cmd.Process.Signal(syscall.SIGIO) case err := <-doneCh: - if err != nil { - t.Fatalf("The program returned but unfortunately with an error: %v", err) - } - if time.Since(beforeStart) < 1*time.Second { - // The program was supposed to sleep for a full (monotonic) second; - // it should not return before that has elapsed. - t.Fatalf("The program stopped too quickly.") - } + checkDoneErr(err) return } } diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index c78b044264742d..c3bd5103205cf6 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -468,6 +468,18 @@ type modulehash struct { // To make sure the map isn't collected, we keep a second reference here. var pinnedTypemaps []map[typeOff]*_type +// aixStaticDataBase (used only on AIX) holds the unrelocated address +// of the data section, set by the linker. +// +// On AIX, an R_ADDR relocation from an RODATA symbol to a DATA symbol +// does not work, as the dynamic loader can change the address of the +// data section, and it is not possible to apply a dynamic relocation +// to RODATA. In order to get the correct address, we need to apply +// the delta between unrelocated and relocated data section addresses. +// aixStaticDataBase is the unrelocated address, and moduledata.data is +// the relocated one. +var aixStaticDataBase uintptr // linker symbol + var firstmoduledata moduledata // linker symbol var lastmoduledatap *moduledata // linker symbol diff --git a/src/runtime/synctest.go b/src/runtime/synctest.go index 498c3b92dd0796..65bb15dfbb0455 100644 --- a/src/runtime/synctest.go +++ b/src/runtime/synctest.go @@ -182,6 +182,8 @@ func synctestRun(f func()) { sg.active++ for { if raceenabled { + // Establish a happens-before relationship between a timer being created, + // and the timer running. raceacquireg(gp, gp.syncGroup.raceaddr()) } unlock(&sg.mu) @@ -205,6 +207,11 @@ func synctestRun(f func()) { total := sg.total unlock(&sg.mu) + if raceenabled { + // Establish a happens-before relationship between bubbled goroutines exiting + // and Run returning. + raceacquireg(gp, gp.syncGroup.raceaddr()) + } if total != 1 { panic("deadlock: all goroutines in bubble are blocked") } diff --git a/src/runtime/sys_linux_s390x.s b/src/runtime/sys_linux_s390x.s index 7da4a5272991d4..2f9d4beda82f64 100644 --- a/src/runtime/sys_linux_s390x.s +++ b/src/runtime/sys_linux_s390x.s @@ -112,9 +112,10 @@ TEXT runtime·usleep(SB),NOSPLIT,$16-4 MOVW $1000000, R3 DIVD R3, R2 MOVD R2, 8(R15) - MOVW $1000, R3 - MULLD R2, R3 + MULLD R2, R3 // Convert sec to usec and subtract SUB R3, R4 + MOVW $1000, R3 + MULLD R3, R4 // Convert remaining usec into nsec. MOVD R4, 16(R15) // nanosleep(&ts, 0) diff --git a/src/runtime/sys_wasm.go b/src/runtime/sys_wasm.go index f88b992e9c39a3..6b40a8d3e94c96 100644 --- a/src/runtime/sys_wasm.go +++ b/src/runtime/sys_wasm.go @@ -34,3 +34,17 @@ func gostartcall(buf *gobuf, fn, ctxt unsafe.Pointer) { buf.pc = uintptr(fn) buf.ctxt = ctxt } + +func notInitialized() // defined in assembly, call notInitialized1 + +// Called if a wasmexport function is called before runtime initialization +// +//go:nosplit +func notInitialized1() { + writeErrStr("runtime: wasmexport function called before runtime initialization\n") + if isarchive || islibrary { + writeErrStr("\tcall _initialize first\n") + } else { + writeErrStr("\tcall _start first\n") + } +} diff --git a/src/runtime/testdata/testprog/crash.go b/src/runtime/testdata/testprog/crash.go index bdc395f652edca..56dd701ffb373c 100644 --- a/src/runtime/testdata/testprog/crash.go +++ b/src/runtime/testdata/testprog/crash.go @@ -19,6 +19,9 @@ func init() { register("StringPanic", StringPanic) register("NilPanic", NilPanic) register("CircularPanic", CircularPanic) + register("ReraisedPanic", ReraisedPanic) + register("ReraisedMiddlePanic", ReraisedMiddlePanic) + register("ReraisedPanicSandwich", ReraisedPanicSandwich) } func test(name string) { @@ -137,3 +140,52 @@ func (e exampleCircleEndError) Error() string { func CircularPanic() { panic(exampleCircleStartError{}) } + +func ReraisedPanic() { + defer func() { + panic(recover()) + }() + panic("message") +} + +func ReraisedMiddlePanic() { + defer func() { + recover() + panic("outer") + }() + func() { + defer func() { + panic(recover()) + }() + func() { + defer func() { + recover() + panic("middle") + }() + panic("inner") + }() + }() +} + +// Panic sandwich: +// +// panic("outer") => +// recovered, panic("inner") => +// panic(recovered outer panic value) +// +// Exercises the edge case where we reraise a panic value, +// but with another panic in the middle. +func ReraisedPanicSandwich() { + var outer any + defer func() { + recover() + panic(outer) + }() + func() { + defer func() { + outer = recover() + panic("inner") + }() + panic("outer") + }() +} diff --git a/src/runtime/traceallocfree.go b/src/runtime/traceallocfree.go index 84188a55c45bad..40f1cfe8ab3d2f 100644 --- a/src/runtime/traceallocfree.go +++ b/src/runtime/traceallocfree.go @@ -9,6 +9,7 @@ package runtime import ( "internal/abi" "internal/runtime/sys" + "internal/trace/tracev2" ) // Batch type values for the alloc/free experiment. @@ -27,7 +28,7 @@ func traceSnapshotMemory(gen uintptr) { // Write a batch containing information that'll be necessary to // interpret the events. var flushed bool - w := unsafeTraceExpWriter(gen, nil, traceExperimentAllocFree) + w := unsafeTraceExpWriter(gen, nil, tracev2.AllocFree) w, flushed = w.ensure(1 + 4*traceBytesPerNumber) if flushed { // Annotate the batch as containing additional info. @@ -89,17 +90,17 @@ func traceSpanTypeAndClass(s *mspan) traceArg { // SpanExists records an event indicating that the span exists. func (tl traceLocker) SpanExists(s *mspan) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSpan, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSpan, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s)) } // SpanAlloc records an event indicating that the span has just been allocated. func (tl traceLocker) SpanAlloc(s *mspan) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSpanAlloc, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSpanAlloc, traceSpanID(s), traceArg(s.npages), traceSpanTypeAndClass(s)) } // SpanFree records an event indicating that the span is about to be freed. func (tl traceLocker) SpanFree(s *mspan) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSpanFree, traceSpanID(s)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSpanFree, traceSpanID(s)) } // traceSpanID creates a trace ID for the span s for the trace. @@ -111,19 +112,19 @@ func traceSpanID(s *mspan) traceArg { // The type is optional, and the size of the slot occupied the object is inferred from the // span containing it. func (tl traceLocker) HeapObjectExists(addr uintptr, typ *abi.Type) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapObject, traceHeapObjectID(addr), tl.rtype(typ)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapObject, traceHeapObjectID(addr), tl.rtype(typ)) } // HeapObjectAlloc records that an object was newly allocated at addr with the provided type. // The type is optional, and the size of the slot occupied the object is inferred from the // span containing it. func (tl traceLocker) HeapObjectAlloc(addr uintptr, typ *abi.Type) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapObjectAlloc, traceHeapObjectID(addr), tl.rtype(typ)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapObjectAlloc, traceHeapObjectID(addr), tl.rtype(typ)) } // HeapObjectFree records that an object at addr is about to be freed. func (tl traceLocker) HeapObjectFree(addr uintptr) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapObjectFree, traceHeapObjectID(addr)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapObjectFree, traceHeapObjectID(addr)) } // traceHeapObjectID creates a trace ID for a heap object at address addr. @@ -134,18 +135,18 @@ func traceHeapObjectID(addr uintptr) traceArg { // GoroutineStackExists records that a goroutine stack already exists at address base with the provided size. func (tl traceLocker) GoroutineStackExists(base, size uintptr) { order := traceCompressStackSize(size) - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoroutineStack, traceGoroutineStackID(base), order) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoroutineStack, traceGoroutineStackID(base), order) } // GoroutineStackAlloc records that a goroutine stack was newly allocated at address base with the provided size.. func (tl traceLocker) GoroutineStackAlloc(base, size uintptr) { order := traceCompressStackSize(size) - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoroutineStackAlloc, traceGoroutineStackID(base), order) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoroutineStackAlloc, traceGoroutineStackID(base), order) } // GoroutineStackFree records that a goroutine stack at address base is about to be freed. func (tl traceLocker) GoroutineStackFree(base uintptr) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoroutineStackFree, traceGoroutineStackID(base)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoroutineStackFree, traceGoroutineStackID(base)) } // traceGoroutineStackID creates a trace ID for the goroutine stack from its base address. diff --git a/src/runtime/traceback_system_test.go b/src/runtime/traceback_system_test.go index ece58e806d51c1..3a3f33bbd41005 100644 --- a/src/runtime/traceback_system_test.go +++ b/src/runtime/traceback_system_test.go @@ -23,8 +23,8 @@ import ( ) // This is the entrypoint of the child process used by -// TestTracebackSystem. It prints a crash report to stdout. -func crash() { +// TestTracebackSystem/panic. It prints a crash report to stdout. +func crashViaPanic() { // Ensure that we get pc=0x%x values in the traceback. debug.SetTraceback("system") writeSentinel(os.Stdout) @@ -37,6 +37,21 @@ func crash() { select {} } +// This is the entrypoint of the child process used by +// TestTracebackSystem/trap. It prints a crash report to stdout. +func crashViaTrap() { + // Ensure that we get pc=0x%x values in the traceback. + debug.SetTraceback("system") + writeSentinel(os.Stdout) + debug.SetCrashOutput(os.Stdout, debug.CrashOptions{}) + + go func() { + // This call is typically inlined. + trap1() + }() + select {} +} + func child1() { child2() } @@ -85,6 +100,20 @@ func child7() { panic("oops") } +func trap1() { + trap2() +} + +var sinkPtr *int + +func trap2() { + trap3(sinkPtr) +} + +func trap3(i *int) { + *i = 42 +} + // TestTracebackSystem tests that the syntax of crash reports produced // by GOTRACEBACK=system (see traceback2) contains a complete, // parseable list of program counters for the running goroutine that @@ -100,46 +129,75 @@ func TestTracebackSystem(t *testing.T) { t.Skip("Can't read source code for this file on Android") } - // Fork+exec the crashing process. - exe, err := os.Executable() - if err != nil { - t.Fatal(err) - } - cmd := testenv.Command(t, exe) - cmd.Env = append(cmd.Environ(), entrypointVar+"=crash") - var stdout, stderr bytes.Buffer - cmd.Stdout = &stdout - cmd.Stderr = &stderr - cmd.Run() // expected to crash - t.Logf("stderr:\n%s\nstdout: %s\n", stderr.Bytes(), stdout.Bytes()) - crash := stdout.String() - - // If the only line is the sentinel, it wasn't a crash. - if strings.Count(crash, "\n") < 2 { - t.Fatalf("child process did not produce a crash report") + tests := []struct { + name string + want string + }{ + { + name: "panic", + want: `redacted.go:0: runtime.gopanic +traceback_system_test.go:100: runtime_test.child7: panic("oops") +traceback_system_test.go:83: runtime_test.child6: child7() // appears in stack trace +traceback_system_test.go:74: runtime_test.child5: child6() // appears in stack trace +traceback_system_test.go:68: runtime_test.child4: child5() +traceback_system_test.go:64: runtime_test.child3: child4() +traceback_system_test.go:60: runtime_test.child2: child3() +traceback_system_test.go:56: runtime_test.child1: child2() +traceback_system_test.go:35: runtime_test.crashViaPanic.func1: child1() +redacted.go:0: runtime.goexit +`, + }, + { + // Test panic via trap. x/telemetry is aware that trap + // PCs follow runtime.sigpanic and need to be + // incremented to offset the decrement done by + // CallersFrames. + name: "trap", + want: `redacted.go:0: runtime.gopanic +redacted.go:0: runtime.panicmem +redacted.go:0: runtime.sigpanic +traceback_system_test.go:114: runtime_test.trap3: *i = 42 +traceback_system_test.go:110: runtime_test.trap2: trap3(sinkPtr) +traceback_system_test.go:104: runtime_test.trap1: trap2() +traceback_system_test.go:50: runtime_test.crashViaTrap.func1: trap1() +redacted.go:0: runtime.goexit +`, + }, } - // Parse the PCs out of the child's crash report. - pcs, err := parseStackPCs(crash) - if err != nil { - t.Fatal(err) - } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Fork+exec the crashing process. + exe, err := os.Executable() + if err != nil { + t.Fatal(err) + } + cmd := testenv.Command(t, exe) + cmd.Env = append(cmd.Environ(), entrypointVar+"="+tc.name) + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + cmd.Run() // expected to crash + t.Logf("stderr:\n%s\nstdout: %s\n", stderr.Bytes(), stdout.Bytes()) + crash := stdout.String() + + // If the only line is the sentinel, it wasn't a crash. + if strings.Count(crash, "\n") < 2 { + t.Fatalf("child process did not produce a crash report") + } - // Unwind the stack using this executable's symbol table. - got := formatStack(pcs) - want := `redacted.go:0: runtime.gopanic -traceback_system_test.go:85: runtime_test.child7: panic("oops") -traceback_system_test.go:68: runtime_test.child6: child7() // appears in stack trace -traceback_system_test.go:59: runtime_test.child5: child6() // appears in stack trace -traceback_system_test.go:53: runtime_test.child4: child5() -traceback_system_test.go:49: runtime_test.child3: child4() -traceback_system_test.go:45: runtime_test.child2: child3() -traceback_system_test.go:41: runtime_test.child1: child2() -traceback_system_test.go:35: runtime_test.crash.func1: child1() -redacted.go:0: runtime.goexit -` - if strings.TrimSpace(got) != strings.TrimSpace(want) { - t.Errorf("got:\n%swant:\n%s", got, want) + // Parse the PCs out of the child's crash report. + pcs, err := parseStackPCs(crash) + if err != nil { + t.Fatal(err) + } + + // Unwind the stack using this executable's symbol table. + got := formatStack(pcs) + if strings.TrimSpace(got) != strings.TrimSpace(tc.want) { + t.Errorf("got:\n%swant:\n%s", got, tc.want) + } + }) } } @@ -154,6 +212,35 @@ redacted.go:0: runtime.goexit // // (Copied from golang.org/x/telemetry/crashmonitor.parseStackPCs.) func parseStackPCs(crash string) ([]uintptr, error) { + // getSymbol parses the symbol name out of a line of the form: + // SYMBOL(ARGS) + // + // Note: SYMBOL may contain parens "pkg.(*T).method". However, type + // parameters are always replaced with ..., so they cannot introduce + // more parens. e.g., "pkg.(*T[...]).method". + // + // ARGS can contain parens. We want the first paren that is not + // immediately preceded by a ".". + // + // TODO(prattmic): This is mildly complicated and is only used to find + // runtime.sigpanic, so perhaps simplify this by checking explicitly + // for sigpanic. + getSymbol := func(line string) (string, error) { + var prev rune + for i, c := range line { + if line[i] != '(' { + prev = c + continue + } + if prev == '.' { + prev = c + continue + } + return line[:i], nil + } + return "", fmt.Errorf("no symbol for stack frame: %s", line) + } + // getPC parses the PC out of a line of the form: // \tFILE:LINE +0xRELPC sp=... fp=... pc=... getPC := func(line string) (uint64, error) { @@ -170,6 +257,9 @@ func parseStackPCs(crash string) ([]uintptr, error) { childSentinel = sentinel() on = false // are we in the first running goroutine? lines = strings.Split(crash, "\n") + symLine = true // within a goroutine, every other line is a symbol or file/line/pc location, starting with symbol. + currSymbol string + prevSymbol string // symbol of the most recent previous frame with a PC. ) for i := 0; i < len(lines); i++ { line := lines[i] @@ -212,21 +302,76 @@ func parseStackPCs(crash string) ([]uintptr, error) { // Note: SYMBOL may contain parens "pkg.(*T).method" // The RELPC is sometimes missing. - // Skip the symbol(args) line. - i++ - if i == len(lines) { - break - } - line = lines[i] + if symLine { + var err error + currSymbol, err = getSymbol(line) + if err != nil { + return nil, fmt.Errorf("error extracting symbol: %v", err) + } - // Parse the PC, and correct for the parent and child's - // different mappings of the text section. - pc, err := getPC(line) - if err != nil { - // Inlined frame, perhaps; skip it. - continue + symLine = false // Next line is FILE:LINE. + } else { + // Parse the PC, and correct for the parent and child's + // different mappings of the text section. + pc, err := getPC(line) + if err != nil { + // Inlined frame, perhaps; skip it. + + // Done with this frame. Next line is a new frame. + // + // Don't update prevSymbol; we only want to + // track frames with a PC. + currSymbol = "" + symLine = true + continue + } + + pc = pc - parentSentinel + childSentinel + + // If the previous frame was sigpanic, then this frame + // was a trap (e.g., SIGSEGV). + // + // Typically all middle frames are calls, and report + // the "return PC". That is, the instruction following + // the CALL where the callee will eventually return to. + // + // runtime.CallersFrames is aware of this property and + // will decrement each PC by 1 to "back up" to the + // location of the CALL, which is the actual line + // number the user expects. + // + // This does not work for traps, as a trap is not a + // call, so the reported PC is not the return PC, but + // the actual PC of the trap. + // + // runtime.Callers is aware of this and will + // intentionally increment trap PCs in order to correct + // for the decrement performed by + // runtime.CallersFrames. See runtime.tracebackPCs and + // runtume.(*unwinder).symPC. + // + // We must emulate the same behavior, otherwise we will + // report the location of the instruction immediately + // prior to the trap, which may be on a different line, + // or even a different inlined functions. + // + // TODO(prattmic): The runtime applies the same trap + // behavior for other "injected calls", see injectCall + // in runtime.(*unwinder).next. Do we want to handle + // those as well? I don't believe we'd ever see + // runtime.asyncPreempt or runtime.debugCallV2 in a + // typical crash. + if prevSymbol == "runtime.sigpanic" { + pc++ + } + + pcs = append(pcs, uintptr(pc)) + + // Done with this frame. Next line is a new frame. + prevSymbol = currSymbol + currSymbol = "" + symLine = true } - pcs = append(pcs, uintptr(pc-parentSentinel+childSentinel)) } return pcs, nil } diff --git a/src/runtime/tracebuf.go b/src/runtime/tracebuf.go index 0849a57809e2ca..63803a90f5a32f 100644 --- a/src/runtime/tracebuf.go +++ b/src/runtime/tracebuf.go @@ -8,6 +8,7 @@ package runtime import ( "internal/runtime/sys" + "internal/trace/tracev2" "unsafe" ) @@ -24,7 +25,7 @@ const traceBytesPerNumber = 10 // we can change it if it's deemed too error-prone. type traceWriter struct { traceLocker - exp traceExperiment + exp tracev2.Experiment *traceBuf } @@ -48,7 +49,7 @@ func (tl traceLocker) writer() traceWriter { gp.throwsplit = true } } - return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][traceNoExperiment]} + return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][tracev2.NoExperiment]} } // unsafeTraceWriter produces a traceWriter that doesn't lock the trace. @@ -70,7 +71,7 @@ func unsafeTraceWriter(gen uintptr, buf *traceBuf) traceWriter { // have any stack growth. // //go:nosplit -func (w traceWriter) event(ev traceEv, args ...traceArg) traceWriter { +func (w traceWriter) event(ev tracev2.EventType, args ...traceArg) traceWriter { // N.B. Everything in this call must be nosplit to maintain // the stack growth related invariants for writing events. @@ -186,10 +187,10 @@ func (w traceWriter) refill() traceWriter { } // Write the buffer's header. - if w.exp == traceNoExperiment { - w.byte(byte(traceEvEventBatch)) + if w.exp == tracev2.NoExperiment { + w.byte(byte(tracev2.EvEventBatch)) } else { - w.byte(byte(traceEvExperimentalBatch)) + w.byte(byte(tracev2.EvExperimentalBatch)) w.byte(byte(w.exp)) } w.varint(uint64(w.gen)) @@ -199,6 +200,27 @@ func (w traceWriter) refill() traceWriter { return w } +// expWriter returns a traceWriter that writes into the current M's stream for +// the given experiment. +func (tl traceLocker) expWriter(exp tracev2.Experiment) traceWriter { + return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][exp], exp: exp} +} + +// unsafeTraceExpWriter produces a traceWriter for experimental trace batches +// that doesn't lock the trace. Data written to experimental batches need not +// conform to the standard trace format. +// +// It should only be used in contexts where either: +// - Another traceLocker is held. +// - trace.gen is prevented from advancing. +// +// This does not have the same stack growth restrictions as traceLocker.writer. +// +// buf may be nil. +func unsafeTraceExpWriter(gen uintptr, buf *traceBuf, exp tracev2.Experiment) traceWriter { + return traceWriter{traceLocker: traceLocker{gen: gen}, traceBuf: buf, exp: exp} +} + // traceBufQueue is a FIFO of traceBufs. type traceBufQueue struct { head, tail *traceBuf @@ -247,7 +269,7 @@ type traceBufHeader struct { type traceBuf struct { _ sys.NotInHeap traceBufHeader - arr [64<<10 - unsafe.Sizeof(traceBufHeader{})]byte // underlying buffer for traceBufHeader.buf + arr [tracev2.MaxBatchSize - unsafe.Sizeof(traceBufHeader{})]byte // underlying buffer for traceBufHeader.buf } // byte appends v to buf. diff --git a/src/runtime/tracecpu.go b/src/runtime/tracecpu.go index c8a6f56ff2f684..092c707f83335f 100644 --- a/src/runtime/tracecpu.go +++ b/src/runtime/tracecpu.go @@ -6,6 +6,8 @@ package runtime +import "internal/trace/tracev2" + // traceInitReadCPU initializes CPU profile -> tracer state for tracing. // // Returns a profBuf for reading from. @@ -114,7 +116,7 @@ func traceStopReadCPU() { // Must not run on the system stack because profBuf.read performs race // operations. func traceReadCPU(gen uintptr) bool { - var pcBuf [traceStackSize]uintptr + var pcBuf [tracev2.MaxFramesPerStack]uintptr data, tags, eof := trace.cpuLogRead[gen%2].read(profBufNonBlocking) for len(data) > 0 { @@ -169,17 +171,17 @@ func traceReadCPU(gen uintptr) bool { // Ensure we have a place to write to. var flushed bool - w, flushed = w.ensure(2 + 5*traceBytesPerNumber /* traceEvCPUSamples + traceEvCPUSample + timestamp + g + m + p + stack ID */) + w, flushed = w.ensure(2 + 5*traceBytesPerNumber /* tracev2.EvCPUSamples + tracev2.EvCPUSample + timestamp + g + m + p + stack ID */) if flushed { // Annotate the batch as containing strings. - w.byte(byte(traceEvCPUSamples)) + w.byte(byte(tracev2.EvCPUSamples)) } // Add the stack to the table. stackID := trace.stackTab[gen%2].put(pcBuf[:nstk]) // Write out the CPU sample. - w.byte(byte(traceEvCPUSample)) + w.byte(byte(tracev2.EvCPUSample)) w.varint(timestamp) w.varint(mpid) w.varint(ppid) diff --git a/src/runtime/traceevent.go b/src/runtime/traceevent.go index 51d23688425ff3..9d1a93d3f9f255 100644 --- a/src/runtime/traceevent.go +++ b/src/runtime/traceevent.go @@ -9,88 +9,7 @@ package runtime import ( "internal/abi" "internal/runtime/sys" -) - -// Event types in the trace, args are given in square brackets. -// -// Naming scheme: -// - Time range event pairs have suffixes "Begin" and "End". -// - "Start", "Stop", "Create", "Destroy", "Block", "Unblock" -// are suffixes reserved for scheduling resources. -// -// NOTE: If you add an event type, make sure you also update all -// tables in this file! -type traceEv uint8 - -const ( - traceEvNone traceEv = iota // unused - - // Structural events. - traceEvEventBatch // start of per-M batch of events [generation, M ID, timestamp, batch length] - traceEvStacks // start of a section of the stack table [...traceEvStack] - traceEvStack // stack table entry [ID, ...{PC, func string ID, file string ID, line #}] - traceEvStrings // start of a section of the string dictionary [...traceEvString] - traceEvString // string dictionary entry [ID, length, string] - traceEvCPUSamples // start of a section of CPU samples [...traceEvCPUSample] - traceEvCPUSample // CPU profiling sample [timestamp, M ID, P ID, goroutine ID, stack ID] - traceEvFrequency // timestamp units per sec [freq] - - // Procs. - traceEvProcsChange // current value of GOMAXPROCS [timestamp, GOMAXPROCS, stack ID] - traceEvProcStart // start of P [timestamp, P ID, P seq] - traceEvProcStop // stop of P [timestamp] - traceEvProcSteal // P was stolen [timestamp, P ID, P seq, M ID] - traceEvProcStatus // P status at the start of a generation [timestamp, P ID, status] - - // Goroutines. - traceEvGoCreate // goroutine creation [timestamp, new goroutine ID, new stack ID, stack ID] - traceEvGoCreateSyscall // goroutine appears in syscall (cgo callback) [timestamp, new goroutine ID] - traceEvGoStart // goroutine starts running [timestamp, goroutine ID, goroutine seq] - traceEvGoDestroy // goroutine ends [timestamp] - traceEvGoDestroySyscall // goroutine ends in syscall (cgo callback) [timestamp] - traceEvGoStop // goroutine yields its time, but is runnable [timestamp, reason, stack ID] - traceEvGoBlock // goroutine blocks [timestamp, reason, stack ID] - traceEvGoUnblock // goroutine is unblocked [timestamp, goroutine ID, goroutine seq, stack ID] - traceEvGoSyscallBegin // syscall enter [timestamp, P seq, stack ID] - traceEvGoSyscallEnd // syscall exit [timestamp] - traceEvGoSyscallEndBlocked // syscall exit and it blocked at some point [timestamp] - traceEvGoStatus // goroutine status at the start of a generation [timestamp, goroutine ID, M ID, status] - - // STW. - traceEvSTWBegin // STW start [timestamp, kind] - traceEvSTWEnd // STW done [timestamp] - - // GC events. - traceEvGCActive // GC active [timestamp, seq] - traceEvGCBegin // GC start [timestamp, seq, stack ID] - traceEvGCEnd // GC done [timestamp, seq] - traceEvGCSweepActive // GC sweep active [timestamp, P ID] - traceEvGCSweepBegin // GC sweep start [timestamp, stack ID] - traceEvGCSweepEnd // GC sweep done [timestamp, swept bytes, reclaimed bytes] - traceEvGCMarkAssistActive // GC mark assist active [timestamp, goroutine ID] - traceEvGCMarkAssistBegin // GC mark assist start [timestamp, stack ID] - traceEvGCMarkAssistEnd // GC mark assist done [timestamp] - traceEvHeapAlloc // gcController.heapLive change [timestamp, heap alloc in bytes] - traceEvHeapGoal // gcController.heapGoal() change [timestamp, heap goal in bytes] - - // Annotations. - traceEvGoLabel // apply string label to current running goroutine [timestamp, label string ID] - traceEvUserTaskBegin // trace.NewTask [timestamp, internal task ID, internal parent task ID, name string ID, stack ID] - traceEvUserTaskEnd // end of a task [timestamp, internal task ID, stack ID] - traceEvUserRegionBegin // trace.{Start,With}Region [timestamp, internal task ID, name string ID, stack ID] - traceEvUserRegionEnd // trace.{End,With}Region [timestamp, internal task ID, name string ID, stack ID] - traceEvUserLog // trace.Log [timestamp, internal task ID, key string ID, stack, value string ID] - - // Coroutines. - traceEvGoSwitch // goroutine switch (coroswitch) [timestamp, goroutine ID, goroutine seq] - traceEvGoSwitchDestroy // goroutine switch and destroy [timestamp, goroutine ID, goroutine seq] - traceEvGoCreateBlocked // goroutine creation (starts blocked) [timestamp, new goroutine ID, new stack ID, stack ID] - - // GoStatus with stack. - traceEvGoStatusStack // goroutine status at the start of a generation, with a stack [timestamp, goroutine ID, M ID, status, stack ID] - - // Batch event for an experimental batch with a custom format. - traceEvExperimentalBatch // start of extra data [experiment ID, generation, M ID, timestamp, batch length, batch data...] + "internal/trace/tracev2" ) // traceArg is a simple wrapper type to help ensure that arguments passed @@ -117,8 +36,8 @@ type traceEventWriter struct { // been Runnable before a GoStart). Otherwise, callers can query the status of either the goroutine // or P and pass the appropriate status. // -// In this case, the default status should be traceGoBad or traceProcBad to help identify bugs sooner. -func (tl traceLocker) eventWriter(goStatus traceGoStatus, procStatus traceProcStatus) traceEventWriter { +// In this case, the default status should be tracev2.GoBad or tracev2.ProcBad to help identify bugs sooner. +func (tl traceLocker) eventWriter(goStatus tracev2.GoStatus, procStatus tracev2.ProcStatus) traceEventWriter { if pp := tl.mp.p.ptr(); pp != nil && !pp.trace.statusWasTraced(tl.gen) && pp.trace.acquireStatus(tl.gen) { tl.writer().writeProcStatus(uint64(pp.id), procStatus, pp.trace.inSweep).end() } @@ -129,7 +48,7 @@ func (tl traceLocker) eventWriter(goStatus traceGoStatus, procStatus traceProcSt } // event writes out a trace event. -func (e traceEventWriter) event(ev traceEv, args ...traceArg) { +func (e traceEventWriter) event(ev tracev2.EventType, args ...traceArg) { e.tl.writer().event(ev, args...).end() } diff --git a/src/runtime/traceexp.go b/src/runtime/traceexp.go deleted file mode 100644 index 13eec0c0b66706..00000000000000 --- a/src/runtime/traceexp.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2024 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. - -package runtime - -// expWriter returns a traceWriter that writes into the current M's stream for -// the given experiment. -func (tl traceLocker) expWriter(exp traceExperiment) traceWriter { - return traceWriter{traceLocker: tl, traceBuf: tl.mp.trace.buf[tl.gen%2][exp], exp: exp} -} - -// unsafeTraceExpWriter produces a traceWriter for experimental trace batches -// that doesn't lock the trace. Data written to experimental batches need not -// conform to the standard trace format. -// -// It should only be used in contexts where either: -// - Another traceLocker is held. -// - trace.gen is prevented from advancing. -// -// This does not have the same stack growth restrictions as traceLocker.writer. -// -// buf may be nil. -func unsafeTraceExpWriter(gen uintptr, buf *traceBuf, exp traceExperiment) traceWriter { - return traceWriter{traceLocker: traceLocker{gen: gen}, traceBuf: buf, exp: exp} -} - -// traceExperiment is an enumeration of the different kinds of experiments supported for tracing. -type traceExperiment uint8 - -const ( - // traceNoExperiment indicates no experiment. - traceNoExperiment traceExperiment = iota - - // traceExperimentAllocFree is an experiment to add alloc/free events to the trace. - traceExperimentAllocFree - - // traceNumExperiments is the number of trace experiments (and 1 higher than - // the highest numbered experiment). - traceNumExperiments -) - -// Experimental events. -const ( - _ traceEv = 127 + iota - - // Experimental events for ExperimentAllocFree. - - // Experimental heap span events. IDs map reversibly to base addresses. - traceEvSpan // heap span exists [timestamp, id, npages, type/class] - traceEvSpanAlloc // heap span alloc [timestamp, id, npages, type/class] - traceEvSpanFree // heap span free [timestamp, id] - - // Experimental heap object events. IDs map reversibly to addresses. - traceEvHeapObject // heap object exists [timestamp, id, type] - traceEvHeapObjectAlloc // heap object alloc [timestamp, id, type] - traceEvHeapObjectFree // heap object free [timestamp, id] - - // Experimental goroutine stack events. IDs map reversibly to addresses. - traceEvGoroutineStack // stack exists [timestamp, id, order] - traceEvGoroutineStackAlloc // stack alloc [timestamp, id, order] - traceEvGoroutineStackFree // stack free [timestamp, id] -) diff --git a/src/runtime/traceruntime.go b/src/runtime/traceruntime.go index 284e61301b19ab..98ac1082a824b2 100644 --- a/src/runtime/traceruntime.go +++ b/src/runtime/traceruntime.go @@ -8,6 +8,7 @@ package runtime import ( "internal/runtime/atomic" + "internal/trace/tracev2" _ "unsafe" // for go:linkname ) @@ -24,11 +25,11 @@ func (s *gTraceState) reset() { // mTraceState is per-M state for the tracer. type mTraceState struct { - seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer. - buf [2][traceNumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2. - link *m // Snapshot of alllink or freelink. - reentered uint32 // Whether we've reentered tracing from within tracing. - oldthrowsplit bool // gp.throwsplit upon calling traceLocker.writer. For debugging. + seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer. + buf [2][tracev2.NumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2. + link *m // Snapshot of alllink or freelink. + reentered uint32 // Whether we've reentered tracing from within tracing. + oldthrowsplit bool // gp.throwsplit upon calling traceLocker.writer. For debugging. } // pTraceState is per-P state for the tracer. @@ -283,7 +284,7 @@ func traceExitedSyscall() { // Gomaxprocs emits a ProcsChange event. func (tl traceLocker) Gomaxprocs(procs int32) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvProcsChange, traceArg(procs), tl.stack(1)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvProcsChange, traceArg(procs), tl.stack(1)) } // ProcStart traces a ProcStart event. @@ -294,14 +295,14 @@ func (tl traceLocker) ProcStart() { // Procs are typically started within the scheduler when there is no user goroutine. If there is a user goroutine, // it must be in _Gsyscall because the only time a goroutine is allowed to have its Proc moved around from under it // is during a syscall. - tl.eventWriter(traceGoSyscall, traceProcIdle).event(traceEvProcStart, traceArg(pp.id), pp.trace.nextSeq(tl.gen)) + tl.eventWriter(tracev2.GoSyscall, tracev2.ProcIdle).event(tracev2.EvProcStart, traceArg(pp.id), pp.trace.nextSeq(tl.gen)) } // ProcStop traces a ProcStop event. func (tl traceLocker) ProcStop(pp *p) { // The only time a goroutine is allowed to have its Proc moved around // from under it is during a syscall. - tl.eventWriter(traceGoSyscall, traceProcRunning).event(traceEvProcStop) + tl.eventWriter(tracev2.GoSyscall, tracev2.ProcRunning).event(tracev2.EvProcStop) } // GCActive traces a GCActive event. @@ -309,7 +310,7 @@ func (tl traceLocker) ProcStop(pp *p) { // Must be emitted by an actively running goroutine on an active P. This restriction can be changed // easily and only depends on where it's currently called. func (tl traceLocker) GCActive() { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCActive, traceArg(trace.seqGC)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCActive, traceArg(trace.seqGC)) // N.B. Only one GC can be running at a time, so this is naturally // serialized by the caller. trace.seqGC++ @@ -320,7 +321,7 @@ func (tl traceLocker) GCActive() { // Must be emitted by an actively running goroutine on an active P. This restriction can be changed // easily and only depends on where it's currently called. func (tl traceLocker) GCStart() { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCBegin, traceArg(trace.seqGC), tl.stack(3)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCBegin, traceArg(trace.seqGC), tl.stack(3)) // N.B. Only one GC can be running at a time, so this is naturally // serialized by the caller. trace.seqGC++ @@ -331,7 +332,7 @@ func (tl traceLocker) GCStart() { // Must be emitted by an actively running goroutine on an active P. This restriction can be changed // easily and only depends on where it's currently called. func (tl traceLocker) GCDone() { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCEnd, traceArg(trace.seqGC)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCEnd, traceArg(trace.seqGC)) // N.B. Only one GC can be running at a time, so this is naturally // serialized by the caller. trace.seqGC++ @@ -341,14 +342,14 @@ func (tl traceLocker) GCDone() { func (tl traceLocker) STWStart(reason stwReason) { // Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the // runtime's state tracking, but it's more accurate and doesn't result in any loss of information. - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSTWBegin, tl.string(reason.String()), tl.stack(2)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSTWBegin, tl.string(reason.String()), tl.stack(2)) } // STWDone traces a STWEnd event. func (tl traceLocker) STWDone() { // Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the // runtime's state tracking, but it's more accurate and doesn't result in any loss of information. - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvSTWEnd) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSTWEnd) } // GCSweepStart prepares to trace a sweep loop. This does not @@ -380,7 +381,7 @@ func (tl traceLocker) GCSweepSpan(bytesSwept uintptr) { pp := tl.mp.p.ptr() if pp.trace.maySweep { if pp.trace.swept == 0 { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCSweepBegin, tl.stack(1)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCSweepBegin, tl.stack(1)) pp.trace.inSweep = true } pp.trace.swept += bytesSwept @@ -398,7 +399,7 @@ func (tl traceLocker) GCSweepDone() { throw("missing traceGCSweepStart") } if pp.trace.inSweep { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCSweepEnd, traceArg(pp.trace.swept), traceArg(pp.trace.reclaimed)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCSweepEnd, traceArg(pp.trace.swept), traceArg(pp.trace.reclaimed)) pp.trace.inSweep = false } pp.trace.maySweep = false @@ -406,22 +407,22 @@ func (tl traceLocker) GCSweepDone() { // GCMarkAssistStart emits a MarkAssistBegin event. func (tl traceLocker) GCMarkAssistStart() { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCMarkAssistBegin, tl.stack(1)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCMarkAssistBegin, tl.stack(1)) } // GCMarkAssistDone emits a MarkAssistEnd event. func (tl traceLocker) GCMarkAssistDone() { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGCMarkAssistEnd) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCMarkAssistEnd) } // GoCreate emits a GoCreate event. func (tl traceLocker) GoCreate(newg *g, pc uintptr, blocked bool) { newg.trace.setStatusTraced(tl.gen) - ev := traceEvGoCreate + ev := tracev2.EvGoCreate if blocked { - ev = traceEvGoCreateBlocked + ev = tracev2.EvGoCreateBlocked } - tl.eventWriter(traceGoRunning, traceProcRunning).event(ev, traceArg(newg.goid), tl.startPC(pc), tl.stack(2)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(ev, traceArg(newg.goid), tl.startPC(pc), tl.stack(2)) } // GoStart emits a GoStart event. @@ -430,10 +431,10 @@ func (tl traceLocker) GoCreate(newg *g, pc uintptr, blocked bool) { func (tl traceLocker) GoStart() { gp := getg().m.curg pp := gp.m.p - w := tl.eventWriter(traceGoRunnable, traceProcRunning) - w.event(traceEvGoStart, traceArg(gp.goid), gp.trace.nextSeq(tl.gen)) + w := tl.eventWriter(tracev2.GoRunnable, tracev2.ProcRunning) + w.event(tracev2.EvGoStart, traceArg(gp.goid), gp.trace.nextSeq(tl.gen)) if pp.ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker { - w.event(traceEvGoLabel, trace.markWorkerLabels[tl.gen%2][pp.ptr().gcMarkWorkerMode]) + w.event(tracev2.EvGoLabel, trace.markWorkerLabels[tl.gen%2][pp.ptr().gcMarkWorkerMode]) } } @@ -441,7 +442,7 @@ func (tl traceLocker) GoStart() { // // TODO(mknyszek): Rename this to GoDestroy. func (tl traceLocker) GoEnd() { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoDestroy) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoDestroy) } // GoSched emits a GoStop event with a GoSched reason. @@ -456,7 +457,7 @@ func (tl traceLocker) GoPreempt() { // GoStop emits a GoStop event with the provided reason. func (tl traceLocker) GoStop(reason traceGoStopReason) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(1)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoStop, traceArg(trace.goStopReasons[tl.gen%2][reason]), tl.stack(1)) } // GoPark emits a GoBlock event with the provided reason. @@ -464,14 +465,14 @@ func (tl traceLocker) GoStop(reason traceGoStopReason) { // TODO(mknyszek): Replace traceBlockReason with waitReason. It's silly // that we have both, and waitReason is way more descriptive. func (tl traceLocker) GoPark(reason traceBlockReason, skip int) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoBlock, traceArg(trace.goBlockReasons[tl.gen%2][reason]), tl.stack(skip)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoBlock, traceArg(trace.goBlockReasons[tl.gen%2][reason]), tl.stack(skip)) } // GoUnpark emits a GoUnblock event. func (tl traceLocker) GoUnpark(gp *g, skip int) { // Emit a GoWaiting status if necessary for the unblocked goroutine. tl.emitUnblockStatus(gp, tl.gen) - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoUnblock, traceArg(gp.goid), gp.trace.nextSeq(tl.gen), tl.stack(skip)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoUnblock, traceArg(gp.goid), gp.trace.nextSeq(tl.gen), tl.stack(skip)) } // GoSwitch emits a GoSwitch event. If destroy is true, the calling goroutine @@ -479,10 +480,10 @@ func (tl traceLocker) GoUnpark(gp *g, skip int) { func (tl traceLocker) GoSwitch(nextg *g, destroy bool) { // Emit a GoWaiting status if necessary for the unblocked goroutine. tl.emitUnblockStatus(nextg, tl.gen) - w := tl.eventWriter(traceGoRunning, traceProcRunning) - ev := traceEvGoSwitch + w := tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning) + ev := tracev2.EvGoSwitch if destroy { - ev = traceEvGoSwitchDestroy + ev = tracev2.EvGoSwitchDestroy } w.event(ev, traceArg(nextg.goid), nextg.trace.nextSeq(tl.gen)) } @@ -494,7 +495,7 @@ func (tl traceLocker) emitUnblockStatus(gp *g, gen uintptr) { // TODO(go.dev/issue/65634): Although it would be nice to add a stack trace here of gp, // we cannot safely do so. gp is in _Gwaiting and so we don't have ownership of its stack. // We can fix this by acquiring the goroutine's scan bit. - tl.writer().writeGoStatus(gp.goid, -1, traceGoWaiting, gp.inMarkAssist, 0).end() + tl.writer().writeGoStatus(gp.goid, -1, tracev2.GoWaiting, gp.inMarkAssist, 0).end() } } @@ -505,7 +506,7 @@ func (tl traceLocker) GoSysCall() { // Scribble down the M that the P is currently attached to. pp := tl.mp.p.ptr() pp.trace.mSyscallID = int64(tl.mp.procid) - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvGoSyscallBegin, pp.trace.nextSeq(tl.gen), tl.stack(1)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoSyscallBegin, pp.trace.nextSeq(tl.gen), tl.stack(1)) } // GoSysExit emits a GoSyscallEnd event, possibly along with a GoSyscallBlocked event @@ -518,15 +519,15 @@ func (tl traceLocker) GoSysCall() { // - The goroutine lost its P and was unable to reacquire it, and is now running without a P. // - The goroutine lost its P and acquired a different one, and is now running with that P. func (tl traceLocker) GoSysExit(lostP bool) { - ev := traceEvGoSyscallEnd - procStatus := traceProcSyscall // Procs implicitly enter traceProcSyscall on GoSyscallBegin. + ev := tracev2.EvGoSyscallEnd + procStatus := tracev2.ProcSyscall // Procs implicitly enter tracev2.ProcSyscall on GoSyscallBegin. if lostP { - ev = traceEvGoSyscallEndBlocked - procStatus = traceProcRunning // If a G has a P when emitting this event, it reacquired a P and is indeed running. + ev = tracev2.EvGoSyscallEndBlocked + procStatus = tracev2.ProcRunning // If a G has a P when emitting this event, it reacquired a P and is indeed running. } else { tl.mp.p.ptr().trace.mSyscallID = -1 } - tl.eventWriter(traceGoSyscall, procStatus).event(ev) + tl.eventWriter(tracev2.GoSyscall, procStatus).event(ev) } // ProcSteal indicates that our current M stole a P from another M. @@ -547,7 +548,7 @@ func (tl traceLocker) ProcSteal(pp *p, inSyscall bool) { if !pp.trace.statusWasTraced(tl.gen) && pp.trace.acquireStatus(tl.gen) { // Careful: don't use the event writer. We never want status or in-progress events // to trigger more in-progress events. - tl.writer().writeProcStatus(uint64(pp.id), traceProcSyscallAbandoned, pp.trace.inSweep).end() + tl.writer().writeProcStatus(uint64(pp.id), tracev2.ProcSyscallAbandoned, pp.trace.inSweep).end() } // The status of the proc and goroutine, if we need to emit one here, is not evident from the @@ -556,18 +557,18 @@ func (tl traceLocker) ProcSteal(pp *p, inSyscall bool) { // ourselves specifically to keep running. The two contexts look different, but can be summarized // fairly succinctly. In the former, we're a regular running goroutine and proc, if we have either. // In the latter, we're a goroutine in a syscall. - goStatus := traceGoRunning - procStatus := traceProcRunning + goStatus := tracev2.GoRunning + procStatus := tracev2.ProcRunning if inSyscall { - goStatus = traceGoSyscall - procStatus = traceProcSyscallAbandoned + goStatus = tracev2.GoSyscall + procStatus = tracev2.ProcSyscallAbandoned } - tl.eventWriter(goStatus, procStatus).event(traceEvProcSteal, traceArg(pp.id), pp.trace.nextSeq(tl.gen), traceArg(mStolenFrom)) + tl.eventWriter(goStatus, procStatus).event(tracev2.EvProcSteal, traceArg(pp.id), pp.trace.nextSeq(tl.gen), traceArg(mStolenFrom)) } // HeapAlloc emits a HeapAlloc event. func (tl traceLocker) HeapAlloc(live uint64) { - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapAlloc, traceArg(live)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapAlloc, traceArg(live)) } // HeapGoal reads the current heap goal and emits a HeapGoal event. @@ -577,7 +578,7 @@ func (tl traceLocker) HeapGoal() { // Heap-based triggering is disabled. heapGoal = 0 } - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvHeapGoal, traceArg(heapGoal)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapGoal, traceArg(heapGoal)) } // GoCreateSyscall indicates that a goroutine has transitioned from dead to GoSyscall. @@ -590,7 +591,7 @@ func (tl traceLocker) GoCreateSyscall(gp *g) { // N.B. We should never trace a status for this goroutine (which we're currently running on), // since we want this to appear like goroutine creation. gp.trace.setStatusTraced(tl.gen) - tl.eventWriter(traceGoBad, traceProcBad).event(traceEvGoCreateSyscall, traceArg(gp.goid)) + tl.eventWriter(tracev2.GoBad, tracev2.ProcBad).event(tracev2.EvGoCreateSyscall, traceArg(gp.goid)) } // GoDestroySyscall indicates that a goroutine has transitioned from GoSyscall to dead. @@ -602,7 +603,7 @@ func (tl traceLocker) GoCreateSyscall(gp *g) { func (tl traceLocker) GoDestroySyscall() { // N.B. If we trace a status here, we must never have a P, and we must be on a goroutine // that is in the syscall state. - tl.eventWriter(traceGoSyscall, traceProcBad).event(traceEvGoDestroySyscall) + tl.eventWriter(tracev2.GoSyscall, tracev2.ProcBad).event(tracev2.EvGoDestroySyscall) } // To access runtime functions from runtime/trace. @@ -617,7 +618,7 @@ func trace_userTaskCreate(id, parentID uint64, taskType string) { // Need to do this check because the caller won't have it. return } - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserTaskBegin, traceArg(id), traceArg(parentID), tl.string(taskType), tl.stack(3)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserTaskBegin, traceArg(id), traceArg(parentID), tl.string(taskType), tl.stack(3)) traceRelease(tl) } @@ -630,7 +631,7 @@ func trace_userTaskEnd(id uint64) { // Need to do this check because the caller won't have it. return } - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserTaskEnd, traceArg(id), tl.stack(2)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserTaskEnd, traceArg(id), tl.stack(2)) traceRelease(tl) } @@ -646,16 +647,16 @@ func trace_userRegion(id, mode uint64, name string) { // Need to do this check because the caller won't have it. return } - var ev traceEv + var ev tracev2.EventType switch mode { case 0: - ev = traceEvUserRegionBegin + ev = tracev2.EvUserRegionBegin case 1: - ev = traceEvUserRegionEnd + ev = tracev2.EvUserRegionEnd default: return } - tl.eventWriter(traceGoRunning, traceProcRunning).event(ev, traceArg(id), tl.string(name), tl.stack(3)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(ev, traceArg(id), tl.string(name), tl.stack(3)) traceRelease(tl) } @@ -668,7 +669,7 @@ func trace_userLog(id uint64, category, message string) { // Need to do this check because the caller won't have it. return } - tl.eventWriter(traceGoRunning, traceProcRunning).event(traceEvUserLog, traceArg(id), tl.string(category), tl.uniqueString(message), tl.stack(3)) + tl.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserLog, traceArg(id), tl.string(category), tl.uniqueString(message), tl.stack(3)) traceRelease(tl) } diff --git a/src/runtime/tracestack.go b/src/runtime/tracestack.go index 225566d1020fc3..bca2d0a88deec4 100644 --- a/src/runtime/tracestack.go +++ b/src/runtime/tracestack.go @@ -9,15 +9,11 @@ package runtime import ( "internal/abi" "internal/goarch" + "internal/trace/tracev2" "unsafe" ) const ( - // Maximum number of PCs in a single stack trace. - // Since events contain only stack id rather than whole stack trace, - // we can allow quite large values here. - traceStackSize = 128 - // logicalStackSentinel is a sentinel value at pcBuf[0] signifying that // pcBuf[1:] holds a logical stack requiring no further processing. Any other // value at pcBuf[0] represents a skip value to apply to the physical stack in @@ -36,7 +32,7 @@ const ( // that this stack trace is being written out for, which needs to be synchronized with // generations moving forward. Prefer traceEventWriter.stack. func traceStack(skip int, gp *g, gen uintptr) uint64 { - var pcBuf [traceStackSize]uintptr + var pcBuf [tracev2.MaxFramesPerStack]uintptr // Figure out gp and mp for the backtrace. var mp *m @@ -55,7 +51,7 @@ func traceStack(skip int, gp *g, gen uintptr) uint64 { // are totally fine for taking a stack trace. They're captured // correctly in goStatusToTraceGoStatus. switch goStatusToTraceGoStatus(status, gp.waitreason) { - case traceGoRunning, traceGoSyscall: + case tracev2.GoRunning, tracev2.GoSyscall: if getg() == gp || mp.curg == gp { break } @@ -147,7 +143,7 @@ func (t *traceStackTable) put(pcs []uintptr) uint64 { // releases all memory and resets state. It must only be called once the caller // can guarantee that there are no more writers to the table. func (t *traceStackTable) dump(gen uintptr) { - stackBuf := make([]uintptr, traceStackSize) + stackBuf := make([]uintptr, tracev2.MaxFramesPerStack) w := unsafeTraceWriter(gen, nil) if root := (*traceMapNode)(t.tab.root.Load()); root != nil { w = dumpStacksRec(root, w, stackBuf) @@ -172,15 +168,15 @@ func dumpStacksRec(node *traceMapNode, w traceWriter, stackBuf []uintptr) traceW // bound is pretty loose, but avoids counting // lots of varint sizes. // - // Add 1 because we might also write traceEvStacks. + // Add 1 because we might also write tracev2.EvStacks. var flushed bool w, flushed = w.ensure(1 + maxBytes) if flushed { - w.byte(byte(traceEvStacks)) + w.byte(byte(tracev2.EvStacks)) } // Emit stack event. - w.byte(byte(traceEvStack)) + w.byte(byte(tracev2.EvStack)) w.varint(uint64(node.id)) w.varint(uint64(len(frames))) for _, frame := range frames { diff --git a/src/runtime/tracestatus.go b/src/runtime/tracestatus.go index 425ac37ba04ee0..4dabc8e562f3de 100644 --- a/src/runtime/tracestatus.go +++ b/src/runtime/tracestatus.go @@ -6,43 +6,9 @@ package runtime -import "internal/runtime/atomic" - -// traceGoStatus is the status of a goroutine. -// -// They correspond directly to the various goroutine -// statuses. -type traceGoStatus uint8 - -const ( - traceGoBad traceGoStatus = iota - traceGoRunnable - traceGoRunning - traceGoSyscall - traceGoWaiting -) - -// traceProcStatus is the status of a P. -// -// They mostly correspond to the various P statuses. -type traceProcStatus uint8 - -const ( - traceProcBad traceProcStatus = iota - traceProcRunning - traceProcIdle - traceProcSyscall - - // traceProcSyscallAbandoned is a special case of - // traceProcSyscall. It's used in the very specific case - // where the first a P is mentioned in a generation is - // part of a ProcSteal event. If that's the first time - // it's mentioned, then there's no GoSyscallBegin to - // connect the P stealing back to at that point. This - // special state indicates this to the parser, so it - // doesn't try to find a GoSyscallEndBlocked that - // corresponds with the ProcSteal. - traceProcSyscallAbandoned +import ( + "internal/runtime/atomic" + "internal/trace/tracev2" ) // writeGoStatus emits a GoStatus event as well as any active ranges on the goroutine. @@ -51,23 +17,23 @@ const ( // have any stack growth. // //go:nosplit -func (w traceWriter) writeGoStatus(goid uint64, mid int64, status traceGoStatus, markAssist bool, stackID uint64) traceWriter { +func (w traceWriter) writeGoStatus(goid uint64, mid int64, status tracev2.GoStatus, markAssist bool, stackID uint64) traceWriter { // The status should never be bad. Some invariant must have been violated. - if status == traceGoBad { + if status == tracev2.GoBad { print("runtime: goid=", goid, "\n") throw("attempted to trace a bad status for a goroutine") } // Trace the status. if stackID == 0 { - w = w.event(traceEvGoStatus, traceArg(goid), traceArg(uint64(mid)), traceArg(status)) + w = w.event(tracev2.EvGoStatus, traceArg(goid), traceArg(uint64(mid)), traceArg(status)) } else { - w = w.event(traceEvGoStatusStack, traceArg(goid), traceArg(uint64(mid)), traceArg(status), traceArg(stackID)) + w = w.event(tracev2.EvGoStatusStack, traceArg(goid), traceArg(uint64(mid)), traceArg(status), traceArg(stackID)) } // Trace any special ranges that are in-progress. if markAssist { - w = w.event(traceEvGCMarkAssistActive, traceArg(goid)) + w = w.event(tracev2.EvGCMarkAssistActive, traceArg(goid)) } return w } @@ -85,26 +51,26 @@ func (w traceWriter) writeProcStatusForP(pp *p, inSTW bool) traceWriter { if !pp.trace.acquireStatus(w.gen) { return w } - var status traceProcStatus + var status tracev2.ProcStatus switch pp.status { case _Pidle, _Pgcstop: - status = traceProcIdle + status = tracev2.ProcIdle if pp.status == _Pgcstop && inSTW { // N.B. a P that is running and currently has the world stopped will be // in _Pgcstop, but we model it as running in the tracer. - status = traceProcRunning + status = tracev2.ProcRunning } case _Prunning: - status = traceProcRunning + status = tracev2.ProcRunning // There's a short window wherein the goroutine may have entered _Gsyscall // but it still owns the P (it's not in _Psyscall yet). The goroutine entering // _Gsyscall is the tracer's signal that the P its bound to is also in a syscall, // so we need to emit a status that matches. See #64318. if w.mp.p.ptr() == pp && w.mp.curg != nil && readgstatus(w.mp.curg)&^_Gscan == _Gsyscall { - status = traceProcSyscall + status = tracev2.ProcSyscall } case _Psyscall: - status = traceProcSyscall + status = tracev2.ProcSyscall default: throw("attempt to trace invalid or unsupported P status") } @@ -121,19 +87,19 @@ func (w traceWriter) writeProcStatusForP(pp *p, inSTW bool) traceWriter { // have any stack growth. // //go:nosplit -func (w traceWriter) writeProcStatus(pid uint64, status traceProcStatus, inSweep bool) traceWriter { +func (w traceWriter) writeProcStatus(pid uint64, status tracev2.ProcStatus, inSweep bool) traceWriter { // The status should never be bad. Some invariant must have been violated. - if status == traceProcBad { + if status == tracev2.ProcBad { print("runtime: pid=", pid, "\n") throw("attempted to trace a bad status for a proc") } // Trace the status. - w = w.event(traceEvProcStatus, traceArg(pid), traceArg(status)) + w = w.event(tracev2.EvProcStatus, traceArg(pid), traceArg(status)) // Trace any special ranges that are in-progress. if inSweep { - w = w.event(traceEvGCSweepActive, traceArg(pid)) + w = w.event(tracev2.EvGCSweepActive, traceArg(pid)) } return w } @@ -146,16 +112,16 @@ func (w traceWriter) writeProcStatus(pid uint64, status traceProcStatus, inSweep // have any stack growth. // //go:nosplit -func goStatusToTraceGoStatus(status uint32, wr waitReason) traceGoStatus { +func goStatusToTraceGoStatus(status uint32, wr waitReason) tracev2.GoStatus { // N.B. Ignore the _Gscan bit. We don't model it in the tracer. - var tgs traceGoStatus + var tgs tracev2.GoStatus switch status &^ _Gscan { case _Grunnable: - tgs = traceGoRunnable + tgs = tracev2.GoRunnable case _Grunning, _Gcopystack: - tgs = traceGoRunning + tgs = tracev2.GoRunning case _Gsyscall: - tgs = traceGoSyscall + tgs = tracev2.GoSyscall case _Gwaiting, _Gpreempted: // There are a number of cases where a G might end up in // _Gwaiting but it's actually running in a non-preemptive @@ -163,9 +129,9 @@ func goStatusToTraceGoStatus(status uint32, wr waitReason) traceGoStatus { // garbage collector. In these cases, we're not going to // emit an event, and we want these goroutines to appear in // the final trace as if they're running, not blocked. - tgs = traceGoWaiting + tgs = tracev2.GoWaiting if status == _Gwaiting && wr.isWaitingForGC() { - tgs = traceGoRunning + tgs = tracev2.GoRunning } case _Gdead: throw("tried to trace dead goroutine") diff --git a/src/runtime/tracestring.go b/src/runtime/tracestring.go index 2585c69cc0b7ff..d486f9efbdf8b5 100644 --- a/src/runtime/tracestring.go +++ b/src/runtime/tracestring.go @@ -6,9 +6,9 @@ package runtime -// Trace strings. +import "internal/trace/tracev2" -const maxTraceStringLen = 1024 +// Trace strings. // traceStringTable is map of string -> unique ID that also manages // writing strings out into the trace. @@ -52,8 +52,8 @@ func (t *traceStringTable) emit(gen uintptr, s string) uint64 { //go:systemstack func (t *traceStringTable) writeString(gen uintptr, id uint64, s string) { // Truncate the string if necessary. - if len(s) > maxTraceStringLen { - s = s[:maxTraceStringLen] + if len(s) > tracev2.MaxEventTrailerDataSize { + s = s[:tracev2.MaxEventTrailerDataSize] } lock(&t.lock) @@ -61,14 +61,14 @@ func (t *traceStringTable) writeString(gen uintptr, id uint64, s string) { // Ensure we have a place to write to. var flushed bool - w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* traceEvStrings + traceEvString + ID + len + string data */) + w, flushed = w.ensure(2 + 2*traceBytesPerNumber + len(s) /* tracev2.EvStrings + tracev2.EvString + ID + len + string data */) if flushed { // Annotate the batch as containing strings. - w.byte(byte(traceEvStrings)) + w.byte(byte(tracev2.EvStrings)) } // Write out the string. - w.byte(byte(traceEvString)) + w.byte(byte(tracev2.EvString)) w.varint(id) w.varint(uint64(len(s))) w.stringData(s) diff --git a/src/runtime/tracetime.go b/src/runtime/tracetime.go index d5ee2b078fc022..bfda0aac9a24d8 100644 --- a/src/runtime/tracetime.go +++ b/src/runtime/tracetime.go @@ -8,6 +8,7 @@ package runtime import ( "internal/goarch" + "internal/trace/tracev2" _ "unsafe" ) @@ -80,10 +81,10 @@ func traceFrequency(gen uintptr) { w := unsafeTraceWriter(gen, nil) // Ensure we have a place to write to. - w, _ = w.ensure(1 + traceBytesPerNumber /* traceEvFrequency + frequency */) + w, _ = w.ensure(1 + traceBytesPerNumber /* tracev2.EvFrequency + frequency */) // Write out the string. - w.byte(byte(traceEvFrequency)) + w.byte(byte(tracev2.EvFrequency)) w.varint(traceClockUnitsPerSecond()) // Immediately flush the buffer. diff --git a/src/runtime/tracetype.go b/src/runtime/tracetype.go index d9e340f64a4cb1..f54f8125784e24 100644 --- a/src/runtime/tracetype.go +++ b/src/runtime/tracetype.go @@ -9,6 +9,7 @@ package runtime import ( "internal/abi" "internal/goarch" + "internal/trace/tracev2" "unsafe" ) @@ -35,7 +36,7 @@ func (t *traceTypeTable) put(typ *abi.Type) uint64 { // releases all memory and resets state. It must only be called once the caller // can guarantee that there are no more writers to the table. func (t *traceTypeTable) dump(gen uintptr) { - w := unsafeTraceExpWriter(gen, nil, traceExperimentAllocFree) + w := unsafeTraceExpWriter(gen, nil, tracev2.AllocFree) if root := (*traceMapNode)(t.tab.root.Load()); root != nil { w = dumpTypesRec(root, w) } diff --git a/src/runtime/type.go b/src/runtime/type.go index 9702164b1a261a..1edf9c9dd6d85c 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -104,6 +104,10 @@ func getGCMaskOnDemand(t *_type) *byte { // in read-only memory currently. addr := unsafe.Pointer(t.GCData) + if GOOS == "aix" { + addr = add(addr, firstmoduledata.data-aixStaticDataBase) + } + for { p := (*byte)(atomic.Loadp(addr)) switch p { diff --git a/src/slices/slices.go b/src/slices/slices.go index 40b4d088b06e39..32029cd8ed297c 100644 --- a/src/slices/slices.go +++ b/src/slices/slices.go @@ -414,6 +414,7 @@ func Grow[S ~[]E, E any](s S, n int) S { panic("cannot be negative") } if n -= cap(s) - len(s); n > 0 { + // This expression allocates only once (see test). s = append(s[:cap(s)], make([]E, n)...)[:len(s)] } return s @@ -483,6 +484,9 @@ func Concat[S ~[]E, E any](slices ...S) S { panic("len out of range") } } + // Use Grow, not make, to round up to the size class: + // the extra space is otherwise unused and helps + // callers that append a few elements to the result. newslice := Grow[S](nil, size) for _, s := range slices { newslice = append(newslice, s...) diff --git a/src/strconv/ftoa.go b/src/strconv/ftoa.go index 6db0d47e0f7ae2..bfe26366e12c13 100644 --- a/src/strconv/ftoa.go +++ b/src/strconv/ftoa.go @@ -44,6 +44,8 @@ var float64info = floatInfo{52, 11, -1023} // zeros are removed). // The special precision -1 uses the smallest number of digits // necessary such that ParseFloat will return f exactly. +// The exponent is written as a decimal integer; +// for all formats other than 'b', it will be at least two digits. func FormatFloat(f float64, fmt byte, prec, bitSize int) string { return string(genericFtoa(make([]byte, 0, max(prec+4, 24)), f, fmt, prec, bitSize)) } diff --git a/src/strconv/quote.go b/src/strconv/quote.go index 1f4929a952c6bf..99c292a8ed5884 100644 --- a/src/strconv/quote.go +++ b/src/strconv/quote.go @@ -378,7 +378,8 @@ func QuotedPrefix(s string) (string, error) { // or backquoted Go string literal, returning the string value // that s quotes. (If s is single-quoted, it would be a Go // character literal; Unquote returns the corresponding -// one-character string. For '' Unquote returns the empty string.) +// one-character string. For an empty character literal +// Unquote returns the empty string.) func Unquote(s string) (string, error) { out, rem, err := unquote(s, true) if len(rem) > 0 { diff --git a/src/strings/example_test.go b/src/strings/example_test.go index 08efcbf68ff0f6..da95d1e58e6499 100644 --- a/src/strings/example_test.go +++ b/src/strings/example_test.go @@ -458,3 +458,93 @@ func ExampleToValidUTF8() { // abc // abc } + +func ExampleLines() { + text := "Hello\nWorld\nGo Programming\n" + for line := range strings.Lines(text) { + fmt.Printf("%q\n", line) + } + + // Output: + // "Hello\n" + // "World\n" + // "Go Programming\n" +} + +func ExampleSplitSeq() { + s := "a,b,c,d" + for part := range strings.SplitSeq(s, ",") { + fmt.Printf("%q\n", part) + } + + // Output: + // "a" + // "b" + // "c" + // "d" +} + +func ExampleSplitAfterSeq() { + s := "a,b,c,d" + for part := range strings.SplitAfterSeq(s, ",") { + fmt.Printf("%q\n", part) + } + + // Output: + // "a," + // "b," + // "c," + // "d" +} + +func ExampleFieldsSeq() { + text := "The quick brown fox" + fmt.Println("Split string into fields:") + for word := range strings.FieldsSeq(text) { + fmt.Printf("%q\n", word) + } + + textWithSpaces := " lots of spaces " + fmt.Println("\nSplit string with multiple spaces:") + for word := range strings.FieldsSeq(textWithSpaces) { + fmt.Printf("%q\n", word) + } + + // Output: + // Split string into fields: + // "The" + // "quick" + // "brown" + // "fox" + // + // Split string with multiple spaces: + // "lots" + // "of" + // "spaces" +} + +func ExampleFieldsFuncSeq() { + text := "The quick brown fox" + fmt.Println("Split on whitespace(similar to FieldsSeq):") + for word := range strings.FieldsFuncSeq(text, unicode.IsSpace) { + fmt.Printf("%q\n", word) + } + + mixedText := "abc123def456ghi" + fmt.Println("\nSplit on digits:") + for word := range strings.FieldsFuncSeq(mixedText, unicode.IsDigit) { + fmt.Printf("%q\n", word) + } + + // Output: + // Split on whitespace(similar to FieldsSeq): + // "The" + // "quick" + // "brown" + // "fox" + // + // Split on digits: + // "abc" + // "def" + // "ghi" +} diff --git a/src/strings/iter.go b/src/strings/iter.go index b9620902bfedb7..3168e59687dc94 100644 --- a/src/strings/iter.go +++ b/src/strings/iter.go @@ -68,7 +68,7 @@ func splitSeq(s, sep string, sepSave int) iter.Seq[string] { } // SplitSeq returns an iterator over all substrings of s separated by sep. -// The iterator yields the same strings that would be returned by Split(s, sep), +// The iterator yields the same strings that would be returned by [Split](s, sep), // but without constructing the slice. // It returns a single-use iterator. func SplitSeq(s, sep string) iter.Seq[string] { @@ -76,7 +76,7 @@ func SplitSeq(s, sep string) iter.Seq[string] { } // SplitAfterSeq returns an iterator over substrings of s split after each instance of sep. -// The iterator yields the same strings that would be returned by SplitAfter(s, sep), +// The iterator yields the same strings that would be returned by [SplitAfter](s, sep), // but without constructing the slice. // It returns a single-use iterator. func SplitAfterSeq(s, sep string) iter.Seq[string] { @@ -84,8 +84,8 @@ func SplitAfterSeq(s, sep string) iter.Seq[string] { } // FieldsSeq returns an iterator over substrings of s split around runs of -// whitespace characters, as defined by unicode.IsSpace. -// The iterator yields the same strings that would be returned by Fields(s), +// whitespace characters, as defined by [unicode.IsSpace]. +// The iterator yields the same strings that would be returned by [Fields](s), // but without constructing the slice. func FieldsSeq(s string) iter.Seq[string] { return func(yield func(string) bool) { @@ -118,7 +118,7 @@ func FieldsSeq(s string) iter.Seq[string] { // FieldsFuncSeq returns an iterator over substrings of s split around runs of // Unicode code points satisfying f(c). -// The iterator yields the same strings that would be returned by FieldsFunc(s), +// The iterator yields the same strings that would be returned by [FieldsFunc](s), // but without constructing the slice. func FieldsFuncSeq(s string, f func(rune) bool) iter.Seq[string] { return func(yield func(string) bool) { diff --git a/src/sync/map_test.go b/src/sync/map_test.go index f12c43a28fc95f..05c81354c87b67 100644 --- a/src/sync/map_test.go +++ b/src/sync/map_test.go @@ -225,15 +225,13 @@ func TestIssue40999(t *testing.T) { // add an initial entry to bias len(m.dirty) above the miss count. m.Store(nil, struct{}{}) - var finalized uint32 + var cleanedUp uint32 - // Set finalizers that count for collected keys. A non-zero count + // Add cleanups that count for collected keys. A non-zero count // indicates that keys have not been leaked. - for atomic.LoadUint32(&finalized) == 0 { + for atomic.LoadUint32(&cleanedUp) == 0 { p := new(int) - runtime.SetFinalizer(p, func(*int) { - atomic.AddUint32(&finalized, 1) - }) + runtime.AddCleanup(p, func(c *uint32) { atomic.AddUint32(c, 1) }, &cleanedUp) m.Store(p, struct{}{}) m.Delete(p) runtime.GC() diff --git a/src/sync/oncefunc_test.go b/src/sync/oncefunc_test.go index 5f0d5640631dd0..daf094571f054b 100644 --- a/src/sync/oncefunc_test.go +++ b/src/sync/oncefunc_test.go @@ -203,9 +203,7 @@ func TestOnceXGC(t *testing.T) { t.Run(n, func(t *testing.T) { buf := make([]byte, 1024) var gc atomic.Bool - runtime.SetFinalizer(&buf[0], func(_ *byte) { - gc.Store(true) - }) + runtime.AddCleanup(&buf[0], func(g *atomic.Bool) { g.Store(true) }, &gc) f := fn(buf) gcwaitfin() if gc.Load() != false { diff --git a/src/sync/pool_test.go b/src/sync/pool_test.go index b6ee983c2989b2..286dcacf3ecc84 100644 --- a/src/sync/pool_test.go +++ b/src/sync/pool_test.go @@ -109,12 +109,10 @@ loop: if try == 1 && testing.Short() { break } - var fin, fin1 uint32 + var cln, cln1 uint32 for i := 0; i < N; i++ { v := new(string) - runtime.SetFinalizer(v, func(vv *string) { - atomic.AddUint32(&fin, 1) - }) + runtime.AddCleanup(v, func(f *uint32) { atomic.AddUint32(f, 1) }, &cln) p.Put(v) } if drain { @@ -126,11 +124,11 @@ loop: runtime.GC() time.Sleep(time.Duration(i*100+10) * time.Millisecond) // 1 pointer can remain on stack or elsewhere - if fin1 = atomic.LoadUint32(&fin); fin1 >= N-1 { + if cln1 = atomic.LoadUint32(&cln); cln1 >= N-1 { continue loop } } - t.Fatalf("only %v out of %v resources are finalized on try %v", fin1, N, try) + t.Fatalf("only %v out of %v resources are cleaned up on try %v", cln1, N, try) } } diff --git a/src/syscall/env_unix.go b/src/syscall/env_unix.go index 8e87e018e82de3..1144ed1416c0ee 100644 --- a/src/syscall/env_unix.go +++ b/src/syscall/env_unix.go @@ -124,7 +124,7 @@ func Setenv(key, value string) error { } func Clearenv() { - envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv + envOnce.Do(copyenv) envLock.Lock() defer envLock.Unlock() diff --git a/src/syscall/exec_linux_test.go b/src/syscall/exec_linux_test.go index 1c2024af4550bf..04973dc9ad306b 100644 --- a/src/syscall/exec_linux_test.go +++ b/src/syscall/exec_linux_test.go @@ -646,7 +646,7 @@ func testAmbientCaps(t *testing.T, userns bool) { u, err := user.Lookup("nobody") if err != nil { - t.Fatal(err) + t.Skip("skipping: the nobody user does not exist; see Issue 71644") } uid, err := strconv.ParseInt(u.Uid, 0, 32) if err != nil { diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go index 74c02cdbe64ada..bbf3de199b4b82 100644 --- a/src/syscall/js/js.go +++ b/src/syscall/js/js.go @@ -212,8 +212,8 @@ func ValueOf(x any) Value { // stringVal copies string x to Javascript and returns a ref. // -// (noescape): This is safe because no references are maintained to the -// Go string x after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string x after the syscall returns. // //go:wasmimport gojs syscall/js.stringVal //go:noescape @@ -302,8 +302,8 @@ func (v Value) Get(p string) Value { // valueGet returns a ref to JavaScript property p of ref v. // -// (noescape): This is safe because no references are maintained to the -// Go string p after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string p after the syscall returns. // //go:wasmimport gojs syscall/js.valueGet //go:noescape @@ -323,8 +323,8 @@ func (v Value) Set(p string, x any) { // valueSet sets property p of ref v to ref x. // -// (noescape): This is safe because no references are maintained to the -// Go string p after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string p after the syscall returns. // //go:wasmimport gojs syscall/js.valueSet //go:noescape @@ -342,8 +342,8 @@ func (v Value) Delete(p string) { // valueDelete deletes the JavaScript property p of ref v. // -// (noescape): This is safe because no references are maintained to the -// Go string p after the syscall returns. +// Using go:noescape is safe because no references are maintained to the +// Go string p after the syscall returns. // //go:wasmimport gojs syscall/js.valueDelete //go:noescape @@ -447,10 +447,10 @@ func (v Value) Call(m string, args ...any) Value { // valueCall does a JavaScript call to the method name m of ref v with the given arguments. // -// (noescape): This is safe because no references are maintained to the -// Go string m after the syscall returns. Additionally, the args slice -// is only used temporarily to collect the JavaScript objects for -// the JavaScript method invocation. +// Using go:noescape is safe because no references are maintained to the +// Go string m after the syscall returns. Additionally, the args slice +// is only used temporarily to collect the JavaScript objects for +// the JavaScript method invocation. // //go:wasmimport gojs syscall/js.valueCall //go:nosplit @@ -477,9 +477,9 @@ func (v Value) Invoke(args ...any) Value { // valueInvoke does a JavaScript call to value v with the given arguments. // -// (noescape): This is safe because the args slice is only used temporarily -// to collect the JavaScript objects for the JavaScript method -// invocation. +// Using go:noescape is safe because the args slice is only used temporarily +// to collect the JavaScript objects for the JavaScript method +// invocation. // //go:wasmimport gojs syscall/js.valueInvoke //go:noescape @@ -505,8 +505,8 @@ func (v Value) New(args ...any) Value { // valueNew uses JavaScript's "new" operator with value v as a constructor and the given arguments. // -// (noescape): This is safe because the args slice is only used temporarily -// to collect the JavaScript objects for the constructor execution. +// Using go:noescape is safe because the args slice is only used temporarily +// to collect the JavaScript objects for the constructor execution. // //go:wasmimport gojs syscall/js.valueNew //go:noescape @@ -614,8 +614,8 @@ func valuePrepareString(v ref) (ref, int) // valueLoadString loads string data located at ref v into byte slice b. // -// (noescape): This is safe because the byte slice is only used as a destination -// for storing the string data and references to it are not maintained. +// Using go:noescape is safe because the byte slice is only used as a destination +// for storing the string data and references to it are not maintained. // //go:wasmimport gojs syscall/js.valueLoadString //go:noescape @@ -658,8 +658,8 @@ func CopyBytesToGo(dst []byte, src Value) int { // copyBytesToGo copies bytes from src to dst. // -// (noescape): This is safe because the dst byte slice is only used as a dst -// copy buffer and no references to it are maintained. +// Using go:noescape is safe because the dst byte slice is only used as a dst +// copy buffer and no references to it are maintained. // //go:wasmimport gojs syscall/js.copyBytesToGo //go:noescape @@ -677,10 +677,10 @@ func CopyBytesToJS(dst Value, src []byte) int { return n } -// copyBytesToJs copies bytes from src to dst. +// copyBytesToJS copies bytes from src to dst. // -// (noescape): This is safe because the src byte slice is only used as a src -// copy buffer and no references to it are maintained. +// Using go:noescape is safe because the src byte slice is only used as a src +// copy buffer and no references to it are maintained. // //go:wasmimport gojs syscall/js.copyBytesToJS //go:noescape diff --git a/src/syscall/syscall_freebsd_386.go b/src/syscall/syscall_freebsd_386.go index 60359e38f6b50d..a217dc758b904f 100644 --- a/src/syscall/syscall_freebsd_386.go +++ b/src/syscall/syscall_freebsd_386.go @@ -36,7 +36,13 @@ func sendfile(outfd int, infd int, offset *int64, count int) (written int, err e var writtenOut uint64 = 0 _, _, e1 := Syscall9(SYS_SENDFILE, uintptr(infd), uintptr(outfd), uintptr(*offset), uintptr((*offset)>>32), uintptr(count), 0, uintptr(unsafe.Pointer(&writtenOut)), 0, 0) - written = int(writtenOut) + // For some reason on the freebsd-386 builder writtenOut + // is modified when the system call returns EINVAL. + // The man page says that the value is only written for + // success, EINTR, or EAGAIN, so only use those cases. + if e1 == 0 || e1 == EINTR || e1 == EAGAIN { + written = int(writtenOut) + } if e1 != 0 { err = e1 diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go index 003f7a538c581f..57d84748fe56d7 100644 --- a/src/syscall/syscall_linux.go +++ b/src/syscall/syscall_linux.go @@ -15,6 +15,7 @@ import ( "internal/itoa" runtimesyscall "internal/runtime/syscall" "runtime" + "slices" "unsafe" ) @@ -134,12 +135,7 @@ func isGroupMember(gid int) bool { return false } - for _, g := range groups { - if g == gid { - return true - } - } - return false + return slices.Contains(groups, gid) } func isCapDacOverrideSet() bool { diff --git a/src/syscall/syscall_linux_mips64x.go b/src/syscall/syscall_linux_mips64x.go index 51f5ead472ef4f..e826e08615e6d1 100644 --- a/src/syscall/syscall_linux_mips64x.go +++ b/src/syscall/syscall_linux_mips64x.go @@ -16,7 +16,6 @@ const ( //sys Dup2(oldfd int, newfd int) (err error) //sys Fchown(fd int, uid int, gid int) (err error) //sys Fstatfs(fd int, buf *Statfs_t) (err error) -//sys fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) = SYS_NEWFSTATAT //sys Ftruncate(fd int, length int64) (err error) //sysnb Getegid() (egid int) //sysnb Geteuid() (euid int) @@ -126,10 +125,22 @@ type stat_t struct { Blocks int64 } +//sys fstatatInternal(dirfd int, path string, stat *stat_t, flags int) (err error) = SYS_NEWFSTATAT //sys fstat(fd int, st *stat_t) (err error) //sys lstat(path string, st *stat_t) (err error) //sys stat(path string, st *stat_t) (err error) +func fstatat(fd int, path string, s *Stat_t, flags int) (err error) { + st := &stat_t{} + err = fstatatInternal(fd, path, st, flags) + fillStat_t(s, st) + return +} + +func Fstatat(fd int, path string, s *Stat_t, flags int) (err error) { + return fstatat(fd, path, s, flags) +} + func Fstat(fd int, s *Stat_t) (err error) { st := &stat_t{} err = fstat(fd, st) diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go index fa340531782bd5..b61889cc438640 100644 --- a/src/syscall/types_windows.go +++ b/src/syscall/types_windows.go @@ -49,6 +49,7 @@ const ( o_DIRECTORY = 0x100000 // used by internal/syscall/windows o_NOFOLLOW_ANY = 0x20000000 // used by internal/syscall/windows o_OPEN_REPARSE = 0x40000000 // used by internal/syscall/windows + o_WRITE_ATTRS = 0x80000000 // used by internal/syscall/windows ) const ( diff --git a/src/syscall/zerrors_linux_386.go b/src/syscall/zerrors_linux_386.go index 9d4ecdeb698056..05b621a2cc51d7 100644 --- a/src/syscall/zerrors_linux_386.go +++ b/src/syscall/zerrors_linux_386.go @@ -1467,7 +1467,7 @@ var errors = [...]string{ 113: "no route to host", 114: "operation already in progress", 115: "operation now in progress", - 116: "stale NFS file handle", + 116: "stale file handle", 117: "structure needs cleaning", 118: "not a XENIX named type file", 119: "no XENIX semaphores available", diff --git a/src/syscall/zerrors_linux_amd64.go b/src/syscall/zerrors_linux_amd64.go index a8b67801e6936f..8bc322a707e8a2 100644 --- a/src/syscall/zerrors_linux_amd64.go +++ b/src/syscall/zerrors_linux_amd64.go @@ -1468,7 +1468,7 @@ var errors = [...]string{ 113: "no route to host", 114: "operation already in progress", 115: "operation now in progress", - 116: "stale NFS file handle", + 116: "stale file handle", 117: "structure needs cleaning", 118: "not a XENIX named type file", 119: "no XENIX semaphores available", diff --git a/src/syscall/zerrors_linux_arm.go b/src/syscall/zerrors_linux_arm.go index 285d26a5618e13..ccbea5f287d6be 100644 --- a/src/syscall/zerrors_linux_arm.go +++ b/src/syscall/zerrors_linux_arm.go @@ -1480,7 +1480,7 @@ var errors = [...]string{ 113: "no route to host", 114: "operation already in progress", 115: "operation now in progress", - 116: "stale NFS file handle", + 116: "stale file handle", 117: "structure needs cleaning", 118: "not a XENIX named type file", 119: "no XENIX semaphores available", diff --git a/src/syscall/zerrors_linux_mips64.go b/src/syscall/zerrors_linux_mips64.go index 6953c92484bc6a..7d505c7bb05618 100644 --- a/src/syscall/zerrors_linux_mips64.go +++ b/src/syscall/zerrors_linux_mips64.go @@ -1744,7 +1744,7 @@ var errors = [...]string{ 148: "no route to host", 149: "operation already in progress", 150: "operation now in progress", - 151: "stale NFS file handle", + 151: "stale file handle", 158: "operation canceled", 159: "no medium found", 160: "wrong medium type", diff --git a/src/syscall/zerrors_linux_mips64le.go b/src/syscall/zerrors_linux_mips64le.go index 6953c92484bc6a..7d505c7bb05618 100644 --- a/src/syscall/zerrors_linux_mips64le.go +++ b/src/syscall/zerrors_linux_mips64le.go @@ -1744,7 +1744,7 @@ var errors = [...]string{ 148: "no route to host", 149: "operation already in progress", 150: "operation now in progress", - 151: "stale NFS file handle", + 151: "stale file handle", 158: "operation canceled", 159: "no medium found", 160: "wrong medium type", diff --git a/src/syscall/zerrors_linux_ppc64.go b/src/syscall/zerrors_linux_ppc64.go index f0661a244556c6..fba5353b3e4b16 100644 --- a/src/syscall/zerrors_linux_ppc64.go +++ b/src/syscall/zerrors_linux_ppc64.go @@ -1800,7 +1800,7 @@ var errors = [...]string{ 113: "no route to host", 114: "operation already in progress", 115: "operation now in progress", - 116: "stale NFS file handle", + 116: "stale file handle", 117: "structure needs cleaning", 118: "not a XENIX named type file", 119: "no XENIX semaphores available", diff --git a/src/syscall/zsyscall_linux_mips64.go b/src/syscall/zsyscall_linux_mips64.go index 0352ea54202d1c..449088c815504a 100644 --- a/src/syscall/zsyscall_linux_mips64.go +++ b/src/syscall/zsyscall_linux_mips64.go @@ -1116,21 +1116,6 @@ func Fstatfs(fd int, buf *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_NEWFSTATAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Ftruncate(fd int, length int64) (err error) { _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), 0) if e1 != 0 { @@ -1638,6 +1623,21 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fstatatInternal(dirfd int, path string, stat *stat_t, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_NEWFSTATAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fstat(fd int, st *stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_mips64le.go b/src/syscall/zsyscall_linux_mips64le.go index 1338b8c1c351c4..048298a39c910d 100644 --- a/src/syscall/zsyscall_linux_mips64le.go +++ b/src/syscall/zsyscall_linux_mips64le.go @@ -1116,21 +1116,6 @@ func Fstatfs(fd int, buf *Statfs_t) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func fstatat(dirfd int, path string, stat *Stat_t, flags int) (err error) { - var _p0 *byte - _p0, err = BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := Syscall6(SYS_NEWFSTATAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func Ftruncate(fd int, length int64) (err error) { _, _, e1 := Syscall(SYS_FTRUNCATE, uintptr(fd), uintptr(length), 0) if e1 != 0 { @@ -1638,6 +1623,21 @@ func utimes(path string, times *[2]Timeval) (err error) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func fstatatInternal(dirfd int, path string, stat *stat_t, flags int) (err error) { + var _p0 *byte + _p0, err = BytePtrFromString(path) + if err != nil { + return + } + _, _, e1 := Syscall6(SYS_NEWFSTATAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(stat)), uintptr(flags), 0, 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func fstat(fd int, st *stat_t) (err error) { _, _, e1 := Syscall(SYS_FSTAT, uintptr(fd), uintptr(unsafe.Pointer(st)), 0) if e1 != 0 { diff --git a/src/testing/allocs.go b/src/testing/allocs.go index 1eeb2d4802c31c..8161fad06fb9ea 100644 --- a/src/testing/allocs.go +++ b/src/testing/allocs.go @@ -18,6 +18,9 @@ import ( // AllocsPerRun sets GOMAXPROCS to 1 during its measurement and will restore // it before returning. func AllocsPerRun(runs int, f func()) (avg float64) { + if parallelStart.Load() != parallelStop.Load() { + panic("testing: AllocsPerRun called during parallel test") + } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) // Warm up the function diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index 78e1b2de6d1247..3a7da9e54012b7 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -5,6 +5,7 @@ package testing import ( + "context" "flag" "fmt" "internal/sysinfo" @@ -78,7 +79,7 @@ type InternalBenchmark struct { } // B is a type passed to [Benchmark] functions to manage benchmark -// timing and to specify the number of iterations to run. +// timing and control the number of iterations. // // A benchmark ends when its Benchmark function returns or calls any of the methods // FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods must be called @@ -133,8 +134,7 @@ func (b *B) StartTimer() { } // StopTimer stops timing a test. This can be used to pause the timer -// while performing complex initialization that you don't -// want to measure. +// while performing steps that you don't want to measure. func (b *B) StopTimer() { if b.timerOn { b.duration += highPrecisionTimeSince(b.start) @@ -182,6 +182,7 @@ func (b *B) ReportAllocs() { func (b *B) runN(n int) { benchmarkLock.Lock() defer benchmarkLock.Unlock() + ctx, cancelCtx := context.WithCancel(context.Background()) defer func() { b.runCleanup(normalPanic) b.checkRaces() @@ -192,6 +193,8 @@ func (b *B) runN(n int) { b.resetRaces() b.N = n b.loopN = 0 + b.ctx = ctx + b.cancelCtx = cancelCtx b.parallelism = 1 b.ResetTimer() @@ -367,6 +370,8 @@ func (b *B) ReportMetric(n float64, unit string) { func (b *B) stopOrScaleBLoop() bool { timeElapsed := highPrecisionTimeSince(b.start) if timeElapsed >= b.benchTime.d { + // Stop the timer so we don't count cleanup time + b.StopTimer() return false } // Loop scaling @@ -387,40 +392,53 @@ func (b *B) loopSlowPath() bool { b.ResetTimer() return true } - // Handles fixed time case + // Handles fixed iterations case if b.benchTime.n > 0 { if b.N < b.benchTime.n { b.N = b.benchTime.n b.loopN++ return true } + b.StopTimer() return false } - // Handles fixed iteration count case + // Handles fixed time case return b.stopOrScaleBLoop() } -// Loop returns true until b.N calls has been made to it. -// -// A benchmark should either use Loop or contain an explicit loop from 0 to b.N, but not both. -// After the benchmark finishes, b.N will contain the total number of calls to op, so the benchmark -// may use b.N to compute other average metrics. +// Loop returns true as long as the benchmark should continue running. // -// The parameters and results of function calls inside the body of "for b.Loop() {...}" are guaranteed -// not to be optimized away. -// Also, the local loop scaling for b.Loop ensures the benchmark function containing the loop will only -// be executed once, i.e. for such construct: +// A typical benchmark is structured like: // -// testing.Benchmark(func(b *testing.B) { -// ...(setup) -// for b.Loop() { -// ...(benchmark logic) -// } -// ...(clean-up) +// func Benchmark(b *testing.B) { +// ... setup ... +// for b.Loop() { +// ... code to measure ... +// } +// ... cleanup ... // } // -// The ...(setup) and ...(clean-up) logic will only be executed once. -// Also benchtime=Nx (N>1) will result in exactly N executions instead of N+1 for b.N style loops. +// Loop resets the benchmark timer the first time it is called in a benchmark, +// so any setup performed prior to starting the benchmark loop does not count +// toward the benchmark measurement. Likewise, when it returns false, it stops +// the timer so cleanup code is not measured. +// +// The compiler never optimizes away calls to functions within the body of a +// "for b.Loop() { ... }" loop. This prevents surprises that can otherwise occur +// if the compiler determines that the result of a benchmarked function is +// unused. The loop must be written in exactly this form, and this only applies +// to calls syntactically between the curly braces of the loop. Optimizations +// are performed as usual in any functions called by the loop. +// +// After Loop returns false, b.N contains the total number of iterations that +// ran, so the benchmark may use b.N to compute other average metrics. +// +// Prior to the introduction of Loop, benchmarks were expected to contain an +// explicit loop from 0 to b.N. Benchmarks should either use Loop or contain a +// loop to b.N, but not both. Loop offers more automatic management of the +// benchmark timer, and runs each benchmark function only once per measurement, +// whereas b.N-based benchmarks must run the benchmark function (and any +// associated setup and cleanup) several times. func (b *B) Loop() bool { if b.loopN != 0 && b.loopN < b.N { b.loopN++ diff --git a/src/testing/benchmark_test.go b/src/testing/benchmark_test.go index 259b70ed4c95e0..e2dd24c839d694 100644 --- a/src/testing/benchmark_test.go +++ b/src/testing/benchmark_test.go @@ -7,6 +7,8 @@ package testing_test import ( "bytes" "cmp" + "context" + "errors" "runtime" "slices" "strings" @@ -127,56 +129,32 @@ func TestRunParallelSkipNow(t *testing.T) { }) } -func TestBLoopHasResults(t *testing.T) { - // Verify that b.N and the b.Loop() iteration count match. - var nIterated int - bRet := testing.Benchmark(func(b *testing.B) { - i := 0 - for b.Loop() { - i++ - } - nIterated = i - }) - if nIterated == 0 { - t.Fatalf("Iteration count zero") - } - if bRet.N != nIterated { - t.Fatalf("Benchmark result N incorrect, got %d want %d", bRet.N, nIterated) - } - // We only need to check duration to make sure benchmark result is written. - if bRet.T == 0 { - t.Fatalf("Benchmark result duration unset") - } -} - -func ExampleB_Loop() { - simpleFunc := func(i int) int { - return i + 1 - } - n := 0 +func TestBenchmarkContext(t *testing.T) { testing.Benchmark(func(b *testing.B) { - // Unlike "for i := range N {...}" style loops, this - // setup logic will only be executed once, so simpleFunc - // will always get argument 1. - n++ - // It behaves just like "for i := range N {...}", except with keeping - // function call parameters and results alive. - for b.Loop() { - // This function call, if was in a normal loop, will be optimized away - // completely, first by inlining, then by dead code elimination. - // In a b.Loop loop, the compiler ensures that this function is not optimized away. - simpleFunc(n) + ctx := b.Context() + if err := ctx.Err(); err != nil { + b.Fatalf("expected non-canceled context, got %v", err) } - // This clean-up will only be executed once, so after the benchmark, the user - // will see n == 2. - n++ - // Use b.ReportMetric as usual just like what a user may do after - // b.N loop. - }) - // We can expect n == 2 here. - // The return value of the above Benchmark could be used just like - // a b.N loop benchmark as well. + var innerCtx context.Context + b.Run("inner", func(b *testing.B) { + innerCtx = b.Context() + if err := innerCtx.Err(); err != nil { + b.Fatalf("expected inner benchmark to not inherit canceled context, got %v", err) + } + }) + b.Run("inner2", func(b *testing.B) { + if !errors.Is(innerCtx.Err(), context.Canceled) { + t.Fatal("expected context of sibling benchmark to be canceled after its test function finished") + } + }) + + t.Cleanup(func() { + if !errors.Is(ctx.Err(), context.Canceled) { + t.Fatal("expected context canceled before cleanup") + } + }) + }) } func ExampleB_RunParallel() { @@ -219,7 +197,7 @@ func ExampleB_ReportMetric() { // specific algorithm (in this case, sorting). testing.Benchmark(func(b *testing.B) { var compares int64 - for i := 0; i < b.N; i++ { + for b.Loop() { s := []int{5, 4, 3, 2, 1} slices.SortFunc(s, func(a, b int) int { compares++ diff --git a/src/testing/cover.go b/src/testing/cover.go index 6ad43ab9ff4a8b..74c97d471e29d1 100644 --- a/src/testing/cover.go +++ b/src/testing/cover.go @@ -6,13 +6,6 @@ package testing -import ( - "fmt" - "internal/goexperiment" - "os" - "sync/atomic" -) - // CoverBlock records the coverage data for a single basic block. // The fields are 1-indexed, as in an editor: The opening line of // the file is number 1, for example. Columns are measured @@ -27,8 +20,6 @@ type CoverBlock struct { Stmts uint16 // Number of statements included in this block. } -var cover Cover - // Cover records information about test coverage checking. // NOTE: This struct is internal to the testing infrastructure and may change. // It is not covered (yet) by the Go 1 compatibility guidelines. @@ -39,86 +30,8 @@ type Cover struct { CoveredPackages string } -// Coverage reports the current code coverage as a fraction in the range [0, 1]. -// If coverage is not enabled, Coverage returns 0. -// -// When running a large set of sequential test cases, checking Coverage after each one -// can be useful for identifying which test cases exercise new code paths. -// It is not a replacement for the reports generated by 'go test -cover' and -// 'go tool cover'. -func Coverage() float64 { - if goexperiment.CoverageRedesign { - return coverage2() - } - var n, d int64 - for _, counters := range cover.Counters { - for i := range counters { - if atomic.LoadUint32(&counters[i]) > 0 { - n++ - } - d++ - } - } - if d == 0 { - return 0 - } - return float64(n) / float64(d) -} - // RegisterCover records the coverage data accumulators for the tests. // NOTE: This function is internal to the testing infrastructure and may change. // It is not covered (yet) by the Go 1 compatibility guidelines. func RegisterCover(c Cover) { - cover = c -} - -// mustBeNil checks the error and, if present, reports it and exits. -func mustBeNil(err error) { - if err != nil { - fmt.Fprintf(os.Stderr, "testing: %s\n", err) - os.Exit(2) - } -} - -// coverReport reports the coverage percentage and writes a coverage profile if requested. -func coverReport() { - if goexperiment.CoverageRedesign { - coverReport2() - return - } - var f *os.File - var err error - if *coverProfile != "" { - f, err = os.Create(toOutputDir(*coverProfile)) - mustBeNil(err) - fmt.Fprintf(f, "mode: %s\n", cover.Mode) - defer func() { mustBeNil(f.Close()) }() - } - - var active, total int64 - var count uint32 - for name, counts := range cover.Counters { - blocks := cover.Blocks[name] - for i := range counts { - stmts := int64(blocks[i].Stmts) - total += stmts - count = atomic.LoadUint32(&counts[i]) // For -mode=atomic. - if count > 0 { - active += stmts - } - if f != nil { - _, err := fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", name, - blocks[i].Line0, blocks[i].Col0, - blocks[i].Line1, blocks[i].Col1, - stmts, - count) - mustBeNil(err) - } - } - } - if total == 0 { - fmt.Println("coverage: [no statements]") - return - } - fmt.Printf("coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages) } diff --git a/src/testing/example_loop_test.go b/src/testing/example_loop_test.go new file mode 100644 index 00000000000000..eff8bab352058f --- /dev/null +++ b/src/testing/example_loop_test.go @@ -0,0 +1,48 @@ +// Copyright 2024 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. + +package testing_test + +import ( + "math/rand/v2" + "testing" +) + +// ExBenchmark shows how to use b.Loop in a benchmark. +// +// (If this were a real benchmark, not an example, this would be named +// BenchmarkSomething.) +func ExBenchmark(b *testing.B) { + // Generate a large random slice to use as an input. + // Since this is done before the first call to b.Loop(), + // it doesn't count toward the benchmark time. + input := make([]int, 128<<10) + for i := range input { + input[i] = rand.Int() + } + + // Perform the benchmark. + for b.Loop() { + // Normally, the compiler would be allowed to optimize away the call + // to sum because it has no side effects and the result isn't used. + // However, inside a b.Loop loop, the compiler ensures function calls + // aren't optimized away. + sum(input) + } + + // Outside the loop, the timer is stopped, so we could perform + // cleanup if necessary without affecting the result. +} + +func sum(data []int) int { + total := 0 + for _, value := range data { + total += value + } + return total +} + +func ExampleB_Loop() { + testing.Benchmark(ExBenchmark) +} diff --git a/src/testing/fstest/mapfs.go b/src/testing/fstest/mapfs.go index 5e3720b0ed6e07..5ce03985e1ee5f 100644 --- a/src/testing/fstest/mapfs.go +++ b/src/testing/fstest/mapfs.go @@ -15,7 +15,7 @@ import ( // A MapFS is a simple in-memory file system for use in tests, // represented as a map from path names (arguments to Open) -// to information about the files or directories they represent. +// to information about the files, directories, or symbolic links they represent. // // The map need not include parent directories for files contained // in the map; those will be synthesized if needed. @@ -34,21 +34,27 @@ type MapFS map[string]*MapFile // A MapFile describes a single file in a [MapFS]. type MapFile struct { - Data []byte // file content + Data []byte // file content or symlink destination Mode fs.FileMode // fs.FileInfo.Mode ModTime time.Time // fs.FileInfo.ModTime Sys any // fs.FileInfo.Sys } var _ fs.FS = MapFS(nil) +var _ fs.ReadLinkFS = MapFS(nil) var _ fs.File = (*openMapFile)(nil) -// Open opens the named file. +// Open opens the named file after following any symbolic links. func (fsys MapFS) Open(name string) (fs.File, error) { if !fs.ValidPath(name) { return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} } - file := fsys[name] + realName, ok := fsys.resolveSymlinks(name) + if !ok { + return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist} + } + + file := fsys[realName] if file != nil && file.Mode&fs.ModeDir == 0 { // Ordinary file return &openMapFile{name, mapFileInfo{path.Base(name), file}, 0}, nil @@ -59,10 +65,8 @@ func (fsys MapFS) Open(name string) (fs.File, error) { // But file can also be non-nil, in case the user wants to set metadata for the directory explicitly. // Either way, we need to construct the list of children of this directory. var list []mapFileInfo - var elem string var need = make(map[string]bool) - if name == "." { - elem = "." + if realName == "." { for fname, f := range fsys { i := strings.Index(fname, "/") if i < 0 { @@ -74,8 +78,7 @@ func (fsys MapFS) Open(name string) (fs.File, error) { } } } else { - elem = name[strings.LastIndex(name, "/")+1:] - prefix := name + "/" + prefix := realName + "/" for fname, f := range fsys { if strings.HasPrefix(fname, prefix) { felem := fname[len(prefix):] @@ -107,9 +110,103 @@ func (fsys MapFS) Open(name string) (fs.File, error) { if file == nil { file = &MapFile{Mode: fs.ModeDir | 0555} } + var elem string + if name == "." { + elem = "." + } else { + elem = name[strings.LastIndex(name, "/")+1:] + } return &mapDir{name, mapFileInfo{elem, file}, list, 0}, nil } +func (fsys MapFS) resolveSymlinks(name string) (_ string, ok bool) { + // Fast path: if a symlink is in the map, resolve it. + if file := fsys[name]; file != nil && file.Mode.Type() == fs.ModeSymlink { + target := string(file.Data) + if path.IsAbs(target) { + return "", false + } + return fsys.resolveSymlinks(path.Join(path.Dir(name), target)) + } + + // Check if each parent directory (starting at root) is a symlink. + for i := 0; i < len(name); { + j := strings.Index(name[i:], "/") + var dir string + if j < 0 { + dir = name + i = len(name) + } else { + dir = name[:i+j] + i += j + } + if file := fsys[dir]; file != nil && file.Mode.Type() == fs.ModeSymlink { + target := string(file.Data) + if path.IsAbs(target) { + return "", false + } + return fsys.resolveSymlinks(path.Join(path.Dir(dir), target) + name[i:]) + } + i += len("/") + } + return name, fs.ValidPath(name) +} + +// ReadLink returns the destination of the named symbolic link. +func (fsys MapFS) ReadLink(name string) (string, error) { + info, err := fsys.lstat(name) + if err != nil { + return "", &fs.PathError{Op: "readlink", Path: name, Err: err} + } + if info.f.Mode.Type() != fs.ModeSymlink { + return "", &fs.PathError{Op: "readlink", Path: name, Err: fs.ErrInvalid} + } + return string(info.f.Data), nil +} + +// Lstat returns a FileInfo describing the named file. +// If the file is a symbolic link, the returned FileInfo describes the symbolic link. +// Lstat makes no attempt to follow the link. +func (fsys MapFS) Lstat(name string) (fs.FileInfo, error) { + info, err := fsys.lstat(name) + if err != nil { + return nil, &fs.PathError{Op: "lstat", Path: name, Err: err} + } + return info, nil +} + +func (fsys MapFS) lstat(name string) (*mapFileInfo, error) { + if !fs.ValidPath(name) { + return nil, fs.ErrNotExist + } + realDir, ok := fsys.resolveSymlinks(path.Dir(name)) + if !ok { + return nil, fs.ErrNotExist + } + elem := path.Base(name) + realName := path.Join(realDir, elem) + + file := fsys[realName] + if file != nil { + return &mapFileInfo{elem, file}, nil + } + + if realName == "." { + return &mapFileInfo{elem, &MapFile{Mode: fs.ModeDir | 0555}}, nil + } + // Maybe a directory. + prefix := realName + "/" + for fname := range fsys { + if strings.HasPrefix(fname, prefix) { + return &mapFileInfo{elem, &MapFile{Mode: fs.ModeDir | 0555}}, nil + } + } + // If the directory name is not in the map, + // and there are no children of the name in the map, + // then the directory is treated as not existing. + return nil, fs.ErrNotExist +} + // fsOnly is a wrapper that hides all but the fs.FS methods, // to avoid an infinite recursion when implementing special // methods in terms of helpers that would use them. diff --git a/src/testing/fstest/mapfs_test.go b/src/testing/fstest/mapfs_test.go index 6381a2e56c99b3..e7ff4180ecc5e4 100644 --- a/src/testing/fstest/mapfs_test.go +++ b/src/testing/fstest/mapfs_test.go @@ -57,3 +57,69 @@ func TestMapFSFileInfoName(t *testing.T) { t.Errorf("MapFS FileInfo.Name want:\n%s\ngot:\n%s\n", want, got) } } + +func TestMapFSSymlink(t *testing.T) { + const fileContent = "If a program is too slow, it must have a loop.\n" + m := MapFS{ + "fortune/k/ken.txt": {Data: []byte(fileContent)}, + "dirlink": {Data: []byte("fortune/k"), Mode: fs.ModeSymlink}, + "linklink": {Data: []byte("dirlink"), Mode: fs.ModeSymlink}, + "ken.txt": {Data: []byte("dirlink/ken.txt"), Mode: fs.ModeSymlink}, + } + if err := TestFS(m, "fortune/k/ken.txt", "dirlink", "ken.txt", "linklink"); err != nil { + t.Error(err) + } + + gotData, err := fs.ReadFile(m, "ken.txt") + if string(gotData) != fileContent || err != nil { + t.Errorf("fs.ReadFile(m, \"ken.txt\") = %q, %v; want %q, ", gotData, err, fileContent) + } + gotLink, err := fs.ReadLink(m, "dirlink") + if want := "fortune/k"; gotLink != want || err != nil { + t.Errorf("fs.ReadLink(m, \"dirlink\") = %q, %v; want %q, ", gotLink, err, fileContent) + } + gotInfo, err := fs.Lstat(m, "dirlink") + if err != nil { + t.Errorf("fs.Lstat(m, \"dirlink\") = _, %v; want _, ", err) + } else { + if got, want := gotInfo.Name(), "dirlink"; got != want { + t.Errorf("fs.Lstat(m, \"dirlink\").Name() = %q; want %q", got, want) + } + if got, want := gotInfo.Mode(), fs.ModeSymlink; got != want { + t.Errorf("fs.Lstat(m, \"dirlink\").Mode() = %v; want %v", got, want) + } + } + gotInfo, err = fs.Stat(m, "dirlink") + if err != nil { + t.Errorf("fs.Stat(m, \"dirlink\") = _, %v; want _, ", err) + } else { + if got, want := gotInfo.Name(), "dirlink"; got != want { + t.Errorf("fs.Stat(m, \"dirlink\").Name() = %q; want %q", got, want) + } + if got, want := gotInfo.Mode(), fs.ModeDir|0555; got != want { + t.Errorf("fs.Stat(m, \"dirlink\").Mode() = %v; want %v", got, want) + } + } + gotInfo, err = fs.Lstat(m, "linklink") + if err != nil { + t.Errorf("fs.Lstat(m, \"linklink\") = _, %v; want _, ", err) + } else { + if got, want := gotInfo.Name(), "linklink"; got != want { + t.Errorf("fs.Lstat(m, \"linklink\").Name() = %q; want %q", got, want) + } + if got, want := gotInfo.Mode(), fs.ModeSymlink; got != want { + t.Errorf("fs.Lstat(m, \"linklink\").Mode() = %v; want %v", got, want) + } + } + gotInfo, err = fs.Stat(m, "linklink") + if err != nil { + t.Errorf("fs.Stat(m, \"linklink\") = _, %v; want _, ", err) + } else { + if got, want := gotInfo.Name(), "linklink"; got != want { + t.Errorf("fs.Stat(m, \"linklink\").Name() = %q; want %q", got, want) + } + if got, want := gotInfo.Mode(), fs.ModeDir|0555; got != want { + t.Errorf("fs.Stat(m, \"linklink\").Mode() = %v; want %v", got, want) + } + } +} diff --git a/src/testing/fstest/testfs.go b/src/testing/fstest/testfs.go index 2917a303b20adf..1fb84b892842cb 100644 --- a/src/testing/fstest/testfs.go +++ b/src/testing/fstest/testfs.go @@ -20,6 +20,9 @@ import ( // TestFS tests a file system implementation. // It walks the entire tree of files in fsys, // opening and checking that each file behaves correctly. +// Symbolic links are not followed, +// but their Lstat values are checked +// if the file system implements [fs.ReadLinkFS]. // It also checks that the file system contains at least the expected files. // As a special case, if no expected files are listed, fsys must be empty. // Otherwise, fsys must contain at least the listed files; it can also contain others. @@ -156,9 +159,14 @@ func (t *fsTester) checkDir(dir string) { path := prefix + name t.checkStat(path, info) t.checkOpen(path) - if info.IsDir() { + switch info.Type() { + case fs.ModeDir: t.checkDir(path) - } else { + case fs.ModeSymlink: + // No further processing. + // Avoid following symlinks to avoid potentially unbounded recursion. + t.files = append(t.files, path) + default: t.checkFile(path) } } @@ -440,6 +448,23 @@ func (t *fsTester) checkStat(path string, entry fs.DirEntry) { t.errorf("%s: fsys.Stat(...) = %s\n\twant %s", path, finfo2, finfo) } } + + if fsys, ok := t.fsys.(fs.ReadLinkFS); ok { + info2, err := fsys.Lstat(path) + if err != nil { + t.errorf("%s: fsys.Lstat: %v", path, err) + return + } + fientry2 := formatInfoEntry(info2) + if fentry != fientry2 { + t.errorf("%s: mismatch:\n\tentry = %s\n\tfsys.Lstat(...) = %s", path, fentry, fientry2) + } + feinfo := formatInfo(einfo) + finfo2 := formatInfo(info2) + if feinfo != finfo2 { + t.errorf("%s: mismatch:\n\tentry.Info() = %s\n\tfsys.Lstat(...) = %s\n", path, feinfo, finfo2) + } + } } // checkDirList checks that two directory lists contain the same files and file info. @@ -570,7 +595,7 @@ func (t *fsTester) checkFileRead(file, desc string, data1, data2 []byte) { } } -// checkBadPath checks that various invalid forms of file's name cannot be opened using t.fsys.Open. +// checkOpen validates file opening behavior by attempting to open and then close the given file path. func (t *fsTester) checkOpen(file string) { t.checkBadPath(file, "Open", func(file string) error { f, err := t.fsys.Open(file) diff --git a/src/testing/fstest/testfs_test.go b/src/testing/fstest/testfs_test.go index 2ef1053a01a646..d6d6d89b89fdaa 100644 --- a/src/testing/fstest/testfs_test.go +++ b/src/testing/fstest/testfs_test.go @@ -28,6 +28,9 @@ func TestSymlink(t *testing.T) { if err := os.Symlink(filepath.Join(tmp, "hello"), filepath.Join(tmp, "hello.link")); err != nil { t.Fatal(err) } + if err := os.Symlink("hello", filepath.Join(tmp, "hello_rel.link")); err != nil { + t.Fatal(err) + } if err := TestFS(tmpfs, "hello", "hello.link"); err != nil { t.Fatal(err) diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go index b41a07f88e0863..dceb786ae2829e 100644 --- a/src/testing/fuzz.go +++ b/src/testing/fuzz.go @@ -5,6 +5,7 @@ package testing import ( + "context" "errors" "flag" "fmt" @@ -293,6 +294,8 @@ func (f *F) Fuzz(ff any) { f.tstate.match.clearSubNames() } + ctx, cancelCtx := context.WithCancel(f.ctx) + // Record the stack trace at the point of this call so that if the subtest // function - which runs in a separate stack - is marked as a helper, we can // continue walking the stack into the parent test. @@ -300,13 +303,15 @@ func (f *F) Fuzz(ff any) { n := runtime.Callers(2, pc[:]) t := &T{ common: common{ - barrier: make(chan bool), - signal: make(chan bool), - name: testName, - parent: &f.common, - level: f.level + 1, - creator: pc[:n], - chatty: f.chatty, + barrier: make(chan bool), + signal: make(chan bool), + name: testName, + parent: &f.common, + level: f.level + 1, + creator: pc[:n], + chatty: f.chatty, + ctx: ctx, + cancelCtx: cancelCtx, }, tstate: f.tstate, } @@ -508,14 +513,17 @@ func runFuzzTests(deps testDeps, fuzzTests []InternalFuzzTarget, deadline time.T continue } } + ctx, cancelCtx := context.WithCancel(context.Background()) f := &F{ common: common{ - signal: make(chan bool), - barrier: make(chan bool), - name: testName, - parent: &root, - level: root.level + 1, - chatty: root.chatty, + signal: make(chan bool), + barrier: make(chan bool), + name: testName, + parent: &root, + level: root.level + 1, + chatty: root.chatty, + ctx: ctx, + cancelCtx: cancelCtx, }, tstate: tstate, fstate: fstate, @@ -590,14 +598,17 @@ func runFuzzing(deps testDeps, fuzzTests []InternalFuzzTarget) (ok bool) { return false } + ctx, cancelCtx := context.WithCancel(context.Background()) f := &F{ common: common{ - signal: make(chan bool), - barrier: nil, // T.Parallel has no effect when fuzzing. - name: testName, - parent: &root, - level: root.level + 1, - chatty: root.chatty, + signal: make(chan bool), + barrier: nil, // T.Parallel has no effect when fuzzing. + name: testName, + parent: &root, + level: root.level + 1, + chatty: root.chatty, + ctx: ctx, + cancelCtx: cancelCtx, }, fstate: fstate, tstate: tstate, diff --git a/src/testing/loop_test.go b/src/testing/loop_test.go new file mode 100644 index 00000000000000..7a1a93fceee7ea --- /dev/null +++ b/src/testing/loop_test.go @@ -0,0 +1,57 @@ +// Copyright 2024 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. + +package testing + +func TestBenchmarkBLoop(t *T) { + var initialStart highPrecisionTime + var firstStart highPrecisionTime + var lastStart highPrecisionTime + var runningEnd bool + runs := 0 + iters := 0 + finalBN := 0 + bRet := Benchmark(func(b *B) { + initialStart = b.start + runs++ + for b.Loop() { + if iters == 0 { + firstStart = b.start + } + lastStart = b.start + iters++ + } + finalBN = b.N + runningEnd = b.timerOn + }) + // Verify that a b.Loop benchmark is invoked just once. + if runs != 1 { + t.Errorf("want runs == 1, got %d", runs) + } + // Verify that at least one iteration ran. + if iters == 0 { + t.Fatalf("no iterations ran") + } + // Verify that b.N, bRet.N, and the b.Loop() iteration count match. + if finalBN != iters || bRet.N != iters { + t.Errorf("benchmark iterations mismatch: %d loop iterations, final b.N=%d, bRet.N=%d", iters, finalBN, bRet.N) + } + // Make sure the benchmark ran for an appropriate amount of time. + if bRet.T < benchTime.d { + t.Fatalf("benchmark ran for %s, want >= %s", bRet.T, benchTime.d) + } + // Verify that the timer is reset on the first loop, and then left alone. + if firstStart == initialStart { + t.Errorf("b.Loop did not reset the timer") + } + if lastStart != firstStart { + t.Errorf("timer was reset during iteration") + } + // Verify that it stopped the timer after the last loop. + if runningEnd { + t.Errorf("timer was still running after last iteration") + } +} + +// See also TestBenchmarkBLoop* in other files. diff --git a/src/testing/newcover.go b/src/testing/newcover.go index ad2f622640e4d9..5a8d728831027f 100644 --- a/src/testing/newcover.go +++ b/src/testing/newcover.go @@ -8,49 +8,52 @@ package testing import ( "fmt" - "internal/goexperiment" "os" _ "unsafe" // for linkname ) -// cover2 variable stores the current coverage mode and a +// cover variable stores the current coverage mode and a // tear-down function to be called at the end of the testing run. -var cover2 struct { +var cover struct { mode string tearDown func(coverprofile string, gocoverdir string) (string, error) snapshotcov func() float64 } -// registerCover2 is invoked during "go test -cover" runs. +// registerCover is invoked during "go test -cover" runs. // It is used to record a 'tear down' function // (to be called when the test is complete) and the coverage mode. -func registerCover2(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) { +func registerCover(mode string, tearDown func(coverprofile string, gocoverdir string) (string, error), snapcov func() float64) { if mode == "" { return } - cover2.mode = mode - cover2.tearDown = tearDown - cover2.snapshotcov = snapcov + cover.mode = mode + cover.tearDown = tearDown + cover.snapshotcov = snapcov } -// coverReport2 invokes a callback in _testmain.go that will +// coverReport reports the coverage percentage and +// writes a coverage profile if requested. +// This invokes a callback in _testmain.go that will // emit coverage data at the point where test execution is complete, // for "go test -cover" runs. -func coverReport2() { - if !goexperiment.CoverageRedesign { - panic("unexpected") - } - if errmsg, err := cover2.tearDown(*coverProfile, *gocoverdir); err != nil { +func coverReport() { + if errmsg, err := cover.tearDown(*coverProfile, *gocoverdir); err != nil { fmt.Fprintf(os.Stderr, "%s: %v\n", errmsg, err) os.Exit(2) } } -// coverage2 returns a rough "coverage percentage so far" -// number to support the testing.Coverage() function. -func coverage2() float64 { - if cover2.mode == "" { +// Coverage reports the current code coverage as a fraction in the range [0, 1]. +// If coverage is not enabled, Coverage returns 0. +// +// When running a large set of sequential test cases, checking Coverage after each one +// can be useful for identifying which test cases exercise new code paths. +// It is not a replacement for the reports generated by 'go test -cover' and +// 'go tool cover'. +func Coverage() float64 { + if cover.mode == "" { return 0.0 } - return cover2.snapshotcov() + return cover.snapshotcov() } diff --git a/src/testing/slogtest/example_test.go b/src/testing/slogtest/example_test.go index 0517a4b8574d8c..88fd2427b2e6d7 100644 --- a/src/testing/slogtest/example_test.go +++ b/src/testing/slogtest/example_test.go @@ -23,7 +23,7 @@ func Example_parsing() { results := func() []map[string]any { var ms []map[string]any - for _, line := range bytes.Split(buf.Bytes(), []byte{'\n'}) { + for line := range bytes.SplitSeq(buf.Bytes(), []byte{'\n'}) { if len(line) == 0 { continue } diff --git a/src/testing/synctest/context_example_test.go b/src/testing/synctest/context_example_test.go new file mode 100644 index 00000000000000..5f7205e50e4c42 --- /dev/null +++ b/src/testing/synctest/context_example_test.go @@ -0,0 +1,78 @@ +// Copyright 2025 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. + +//go:build goexperiment.synctest + +package synctest_test + +import ( + "context" + "fmt" + "testing/synctest" + "time" +) + +// This example demonstrates testing the context.AfterFunc function. +// +// AfterFunc registers a function to execute in a new goroutine +// after a context is canceled. +// +// The test verifies that the function is not run before the context is canceled, +// and is run after the context is canceled. +func Example_contextAfterFunc() { + synctest.Run(func() { + // Create a context.Context which can be canceled. + ctx, cancel := context.WithCancel(context.Background()) + + // context.AfterFunc registers a function to be called + // when a context is canceled. + afterFuncCalled := false + context.AfterFunc(ctx, func() { + afterFuncCalled = true + }) + + // The context has not been canceled, so the AfterFunc is not called. + synctest.Wait() + fmt.Printf("before context is canceled: afterFuncCalled=%v\n", afterFuncCalled) + + // Cancel the context and wait for the AfterFunc to finish executing. + // Verify that the AfterFunc ran. + cancel() + synctest.Wait() + fmt.Printf("after context is canceled: afterFuncCalled=%v\n", afterFuncCalled) + + // Output: + // before context is canceled: afterFuncCalled=false + // after context is canceled: afterFuncCalled=true + }) +} + +// This example demonstrates testing the context.WithTimeout function. +// +// WithTimeout creates a context which is canceled after a timeout. +// +// The test verifies that the context is not canceled before the timeout expires, +// and is canceled after the timeout expires. +func Example_contextWithTimeout() { + synctest.Run(func() { + // Create a context.Context which is canceled after a timeout. + const timeout = 5 * time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + // Wait just less than the timeout. + time.Sleep(timeout - time.Nanosecond) + synctest.Wait() + fmt.Printf("before timeout: ctx.Err() = %v\n", ctx.Err()) + + // Wait the rest of the way until the timeout. + time.Sleep(time.Nanosecond) + synctest.Wait() + fmt.Printf("after timeout: ctx.Err() = %v\n", ctx.Err()) + + // Output: + // before timeout: ctx.Err() = + // after timeout: ctx.Err() = context deadline exceeded + }) +} diff --git a/src/testing/synctest/http_example_test.go b/src/testing/synctest/http_example_test.go new file mode 100644 index 00000000000000..ec503a9fa26aeb --- /dev/null +++ b/src/testing/synctest/http_example_test.go @@ -0,0 +1,101 @@ +// Copyright 2025 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. + +//go:build goexperiment.synctest + +package synctest_test + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io" + "net" + "net/http" + "strings" + "testing/synctest" + "time" +) + +// This example demonstrates testing [http.Transport]'s 100 Continue handling. +// +// An HTTP client sending a request can include an "Expect: 100-continue" header +// to tell the server that the client has additional data to send. +// The server may then respond with an 100 Continue information response +// to request the data, or some other status to tell the client the data is not needed. +// For example, a client uploading a large file might use this feature to confirm +// that the server is willing to accept the file before sending it. +// +// This test confirms that when sending an "Expect: 100-continue" header +// the HTTP client does not send a request's content before the server requests it, +// and that it does send the content after receiving a 100 Continue response. +func Example_httpTransport100Continue() { + synctest.Run(func() { + // Create an in-process fake network connection. + // We cannot use a loopback network connection for this test, + // because goroutines blocked on network I/O prevent a synctest + // bubble from becoming idle. + srvConn, cliConn := net.Pipe() + defer cliConn.Close() + defer srvConn.Close() + + tr := &http.Transport{ + // Use the fake network connection created above. + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + return cliConn, nil + }, + // Enable "Expect: 100-continue" handling. + ExpectContinueTimeout: 5 * time.Second, + } + + // Send a request with the "Expect: 100-continue" header set. + // Send it in a new goroutine, since it won't complete until the end of the test. + body := "request body" + go func() { + req, err := http.NewRequest("PUT", "http://test.tld/", strings.NewReader(body)) + if err != nil { + panic(err) + } + req.Header.Set("Expect", "100-continue") + resp, err := tr.RoundTrip(req) + if err != nil { + fmt.Printf("RoundTrip: unexpected error %v\n", err) + } else { + resp.Body.Close() + } + }() + + // Read the request headers sent by the client. + req, err := http.ReadRequest(bufio.NewReader(srvConn)) + if err != nil { + fmt.Printf("ReadRequest: %v\n", err) + return + } + + // Start a new goroutine copying the body sent by the client into a buffer. + // Wait for all goroutines in the bubble to block and verify that we haven't + // read anything from the client yet. + var gotBody bytes.Buffer + go io.Copy(&gotBody, req.Body) + synctest.Wait() + fmt.Printf("before sending 100 Continue, read body: %q\n", gotBody.String()) + + // Write a "100 Continue" response to the client and verify that + // it sends the request body. + srvConn.Write([]byte("HTTP/1.1 100 Continue\r\n\r\n")) + synctest.Wait() + fmt.Printf("after sending 100 Continue, read body: %q\n", gotBody.String()) + + // Finish up by sending the "200 OK" response to conclude the request. + srvConn.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) + + // We started several goroutines during the test. + // The synctest.Run call will wait for all of them to exit before returning. + }) + + // Output: + // before sending 100 Continue, read body: "" + // after sending 100 Continue, read body: "request body" +} diff --git a/src/testing/testing.go b/src/testing/testing.go index e353ceb74119a3..8b0915a0ef7901 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -72,27 +72,24 @@ // A sample benchmark function looks like this: // // func BenchmarkRandInt(b *testing.B) { -// for range b.N { +// for b.Loop() { // rand.Int() // } // } // -// The benchmark function must run the target code b.N times. -// It is called multiple times with b.N adjusted until the -// benchmark function lasts long enough to be timed reliably. // The output // // BenchmarkRandInt-8 68453040 17.8 ns/op // -// means that the loop ran 68453040 times at a speed of 17.8 ns per loop. +// means that the body of the loop ran 68453040 times at a speed of 17.8 ns per loop. // -// If a benchmark needs some expensive setup before running, the timer -// may be reset: +// Only the body of the loop is timed, so benchmarks may do expensive +// setup before calling b.Loop, which will not be counted toward the +// benchmark measurement: // // func BenchmarkBigLen(b *testing.B) { // big := NewBig() -// b.ResetTimer() -// for range b.N { +// for b.Loop() { // big.Len() // } // } @@ -120,6 +117,37 @@ // In particular, https://golang.org/x/perf/cmd/benchstat performs // statistically robust A/B comparisons. // +// # b.N-style benchmarks +// +// Prior to the introduction of [B.Loop], benchmarks were written in a +// different style using B.N. For example: +// +// func BenchmarkRandInt(b *testing.B) { +// for range b.N { +// rand.Int() +// } +// } +// +// In this style of benchmark, the benchmark function must run +// the target code b.N times. The benchmark function is called +// multiple times with b.N adjusted until the benchmark function +// lasts long enough to be timed reliably. This also means any setup +// done before the loop may be run several times. +// +// If a benchmark needs some expensive setup before running, the timer +// should be explicitly reset: +// +// func BenchmarkBigLen(b *testing.B) { +// big := NewBig() +// b.ResetTimer() +// for range b.N { +// big.Len() +// } +// } +// +// New benchmarks should prefer using [B.Loop], which is more robust +// and more efficient. +// // # Examples // // The package also runs and verifies example code. Example functions may @@ -375,7 +403,6 @@ import ( "errors" "flag" "fmt" - "internal/goexperiment" "internal/race" "io" "math/rand" @@ -397,6 +424,11 @@ import ( var initRan bool +var ( + parallelStart atomic.Int64 // number of parallel tests started + parallelStop atomic.Int64 // number of parallel tests stopped +) + // Init registers testing flags. These flags are automatically registered by // the "go test" command before running test functions, so Init is only needed // when calling functions such as Benchmark without using "go test". @@ -672,10 +704,7 @@ func Testing() bool { // values are "set", "count", or "atomic". The return value will be // empty if test coverage is not enabled. func CoverMode() string { - if goexperiment.CoverageRedesign { - return cover2.mode - } - return cover.Mode + return cover.mode } // Verbose reports whether the -test.v flag is set. @@ -1357,10 +1386,10 @@ func (c *common) Chdir(dir string) { } // Context returns a context that is canceled just before -// [T.Cleanup]-registered functions are called. +// Cleanup-registered functions are called. // // Cleanup functions can wait for any resources -// that shut down on Context.Done before the test completes. +// that shut down on Context.Done before the test or benchmark completes. func (c *common) Context() context.Context { c.checkFuzzFn("Context") return c.ctx @@ -1512,7 +1541,6 @@ func (t *T) Parallel() { if t.denyParallel { panic(parallelConflict) } - t.isParallel = true if t.parent.barrier == nil { // T.Parallel has no effect when fuzzing. // Multiple processes may run in parallel, but only one input can run at a @@ -1520,6 +1548,8 @@ func (t *T) Parallel() { return } + t.isParallel = true + // We don't want to include the time we spend waiting for serial tests // in the test duration. Record the elapsed time thus far and reset the // timer afterwards. @@ -1548,6 +1578,7 @@ func (t *T) Parallel() { t.signal <- true // Release calling test. <-t.parent.barrier // Wait for the parent test to complete. t.tstate.waitParallel() + parallelStart.Add(1) if t.chatty != nil { t.chatty.Updatef(t.name, "=== CONT %s\n", t.name) @@ -1683,6 +1714,9 @@ func tRunner(t *T, fn func(t *T)) { panic(err) } running.Delete(t.name) + if t.isParallel { + parallelStop.Add(1) + } t.signal <- signal }() @@ -1993,7 +2027,7 @@ type testDeps interface { // It is not meant to be called directly and is not subject to the Go 1 compatibility document. // It may change signature from release to release. func MainStart(deps testDeps, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M { - registerCover2(deps.InitRuntimeCoverage()) + registerCover(deps.InitRuntimeCoverage()) Init() return &M{ deps: deps, @@ -2477,7 +2511,7 @@ func (m *M) stopAlarm() { } func parseCpuList() { - for _, val := range strings.Split(*cpuListStr, ",") { + for val := range strings.SplitSeq(*cpuListStr, ",") { val = strings.TrimSpace(val) if val == "" { continue diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go index 797728c7a89b0a..addf6cad91d667 100644 --- a/src/testing/testing_test.go +++ b/src/testing/testing_test.go @@ -894,7 +894,7 @@ func TestRunningTestsInCleanup(t *testing.T) { func parseRunningTests(out []byte) (runningTests []string, ok bool) { inRunningTests := false - for _, line := range strings.Split(string(out), "\n") { + for line := range strings.SplitSeq(string(out), "\n") { if inRunningTests { // Package testing adds one tab, the panic printer adds another. if trimmed, ok := strings.CutPrefix(line, "\t\t"); ok { diff --git a/src/text/scanner/scanner.go b/src/text/scanner/scanner.go index 6ae7a9b987423b..316fb4380f753c 100644 --- a/src/text/scanner/scanner.go +++ b/src/text/scanner/scanner.go @@ -50,7 +50,7 @@ func (pos Position) String() string { // to configure a [Scanner] such that it only recognizes (Go) identifiers, // integers, and skips comments, set the Scanner's Mode field to: // -// ScanIdents | ScanInts | SkipComments +// ScanIdents | ScanInts | ScanComments | SkipComments // // With the exceptions of comments, which are skipped if SkipComments is // set, unrecognized tokens are not ignored. Instead, the scanner simply diff --git a/src/text/template/exec.go b/src/text/template/exec.go index ed6ae43671a127..7a67ec6824f5e5 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -333,7 +333,7 @@ func isTrue(val reflect.Value) (truth, ok bool) { truth = val.Bool() case reflect.Complex64, reflect.Complex128: truth = val.Complex() != 0 - case reflect.Chan, reflect.Func, reflect.Pointer, reflect.Interface: + case reflect.Chan, reflect.Func, reflect.Pointer, reflect.UnsafePointer, reflect.Interface: truth = !val.IsNil() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: truth = val.Int() != 0 diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go index 03ec9d759a0dd7..0a0be43baa4f49 100644 --- a/src/text/template/exec_test.go +++ b/src/text/template/exec_test.go @@ -15,6 +15,7 @@ import ( "strings" "sync" "testing" + "unsafe" ) var debug = flag.Bool("debug", false, "show the errors produced by the tests") @@ -75,6 +76,8 @@ type T struct { PS *string PSI *[]int NIL *int + UPI unsafe.Pointer + EmptyUPI unsafe.Pointer // Function (not method) BinaryFunc func(string, string) string VariadicFunc func(...string) string @@ -166,6 +169,7 @@ var tVal = &T{ PI: newInt(23), PS: newString("a string"), PSI: newIntSlice(21, 22, 23), + UPI: newUnsafePointer(23), BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, @@ -192,6 +196,10 @@ func newInt(n int) *int { return &n } +func newUnsafePointer(n int) unsafe.Pointer { + return unsafe.Pointer(&n) +} + func newString(s string) *string { return &s } @@ -443,6 +451,10 @@ var execTests = []execTest{ {"if 0.0", "{{if .FloatZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, {"if 1.5i", "{{if 1.5i}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, {"if 0.0i", "{{if .ComplexZero}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if nonNilPointer", "{{if .PI}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if nilPointer", "{{if .NIL}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, + {"if UPI", "{{if .UPI}}NON-ZERO{{else}}ZERO{{end}}", "NON-ZERO", tVal, true}, + {"if EmptyUPI", "{{if .EmptyUPI}}NON-ZERO{{else}}ZERO{{end}}", "ZERO", tVal, true}, {"if emptystring", "{{if ``}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, {"if string", "{{if `notempty`}}NON-EMPTY{{else}}EMPTY{{end}}", "NON-EMPTY", tVal, true}, {"if emptyslice", "{{if .SIEmpty}}NON-EMPTY{{else}}EMPTY{{end}}", "EMPTY", tVal, true}, @@ -1493,6 +1505,44 @@ func TestBadFuncNames(t *testing.T) { } } +func TestIsTrue(t *testing.T) { + var nil_ptr *int + var nil_chan chan int + tests := []struct{ + v any + want bool + }{ + {1, true}, + {0, false}, + {uint8(1), true}, + {uint8(0), false}, + {float64(1.0), true}, + {float64(0.0), false}, + {complex64(1.0), true}, + {complex64(0.0), false}, + {true, true}, + {false, false}, + {[2]int{1,2}, true}, + {[0]int{}, false}, + {[]byte("abc"), true}, + {[]byte(""), false}, + {map[string] int {"a": 1, "b": 2}, true}, + {map[string] int {}, false}, + {make(chan int), true}, + {nil_chan, false}, + {new(int), true}, + {nil_ptr, false}, + {unsafe.Pointer(new(int)), true}, + {unsafe.Pointer(nil_ptr), false}, + } + for _, test_case := range tests { + got, _ := IsTrue(test_case.v) + if got != test_case.want { + t.Fatalf("expect result %v, got %v", test_case.want, got) + } + } +} + func testBadFuncName(name string, t *testing.T) { t.Helper() defer func() { diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go index 7d63cf8b7bb6db..4d733135fe5a85 100644 --- a/src/text/template/funcs.go +++ b/src/text/template/funcs.go @@ -409,7 +409,6 @@ func not(arg reflect.Value) bool { var ( errBadComparisonType = errors.New("invalid type for comparison") - errBadComparison = errors.New("incompatible types for comparison") errNoComparison = errors.New("missing argument for comparison") ) @@ -487,7 +486,7 @@ func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { truth = arg.Int() >= 0 && arg1.Uint() == uint64(arg.Int()) default: if arg1.IsValid() && arg.IsValid() { - return false, errBadComparison + return false, fmt.Errorf("incompatible types for comparison: %v and %v", arg1.Type(), arg.Type()) } } } else { @@ -553,7 +552,7 @@ func lt(arg1, arg2 reflect.Value) (bool, error) { case k1 == uintKind && k2 == intKind: truth = arg2.Int() >= 0 && arg1.Uint() < uint64(arg2.Int()) default: - return false, errBadComparison + return false, fmt.Errorf("incompatible types for comparison: %v and %v", arg1.Type(), arg2.Type()) } } else { switch k1 { diff --git a/src/unicode/utf8/utf8_test.go b/src/unicode/utf8/utf8_test.go index 69362d2cf1af6c..865167731f990b 100644 --- a/src/unicode/utf8/utf8_test.go +++ b/src/unicode/utf8/utf8_test.go @@ -12,16 +12,6 @@ import ( . "unicode/utf8" ) -// Validate the constants redefined from unicode. -func init() { - if MaxRune != unicode.MaxRune { - panic("utf8.MaxRune is wrong") - } - if RuneError != unicode.ReplacementChar { - panic("utf8.RuneError is wrong") - } -} - // Validate the constants redefined from unicode. func TestConstants(t *testing.T) { if MaxRune != unicode.MaxRune { diff --git a/src/unique/handle.go b/src/unique/handle.go index 46f2da3ddcbd95..520ab70f8c7d83 100644 --- a/src/unique/handle.go +++ b/src/unique/handle.go @@ -89,7 +89,7 @@ func Make[T comparable](value T) Handle[T] { } var ( - // uniqueMaps is an index of type-specific sync maps used for unique.Make. + // uniqueMaps is an index of type-specific concurrent maps used for unique.Make. // // The two-level map might seem odd at first since the HashTrieMap could have "any" // as its key type, but the issue is escape analysis. We do not want to force lookups diff --git a/src/vendor/golang.org/x/net/route/address.go b/src/vendor/golang.org/x/net/route/address.go deleted file mode 100644 index b649f431410159..00000000000000 --- a/src/vendor/golang.org/x/net/route/address.go +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright 2016 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. - -//go:build darwin || dragonfly || freebsd || netbsd || openbsd - -package route - -import ( - "runtime" - "syscall" -) - -// An Addr represents an address associated with packet routing. -type Addr interface { - // Family returns an address family. - Family() int -} - -// A LinkAddr represents a link-layer address. -type LinkAddr struct { - Index int // interface index when attached - Name string // interface name when attached - Addr []byte // link-layer address when attached -} - -// Family implements the Family method of Addr interface. -func (a *LinkAddr) Family() int { return syscall.AF_LINK } - -func (a *LinkAddr) lenAndSpace() (int, int) { - l := 8 + len(a.Name) + len(a.Addr) - return l, roundup(l) -} - -func (a *LinkAddr) marshal(b []byte) (int, error) { - l, ll := a.lenAndSpace() - if len(b) < ll { - return 0, errShortBuffer - } - nlen, alen := len(a.Name), len(a.Addr) - if nlen > 255 || alen > 255 { - return 0, errInvalidAddr - } - b[0] = byte(l) - b[1] = syscall.AF_LINK - if a.Index > 0 { - nativeEndian.PutUint16(b[2:4], uint16(a.Index)) - } - data := b[8:] - if nlen > 0 { - b[5] = byte(nlen) - copy(data[:nlen], a.Name) - data = data[nlen:] - } - if alen > 0 { - b[6] = byte(alen) - copy(data[:alen], a.Addr) - data = data[alen:] - } - return ll, nil -} - -func parseLinkAddr(b []byte) (Addr, error) { - if len(b) < 8 { - return nil, errInvalidAddr - } - _, a, err := parseKernelLinkAddr(syscall.AF_LINK, b[4:]) - if err != nil { - return nil, err - } - a.(*LinkAddr).Index = int(nativeEndian.Uint16(b[2:4])) - return a, nil -} - -// parseKernelLinkAddr parses b as a link-layer address in -// conventional BSD kernel form. -func parseKernelLinkAddr(_ int, b []byte) (int, Addr, error) { - // The encoding looks like the following: - // +----------------------------+ - // | Type (1 octet) | - // +----------------------------+ - // | Name length (1 octet) | - // +----------------------------+ - // | Address length (1 octet) | - // +----------------------------+ - // | Selector length (1 octet) | - // +----------------------------+ - // | Data (variable) | - // +----------------------------+ - // - // On some platforms, all-bit-one of length field means "don't - // care". - nlen, alen, slen := int(b[1]), int(b[2]), int(b[3]) - if nlen == 0xff { - nlen = 0 - } - if alen == 0xff { - alen = 0 - } - if slen == 0xff { - slen = 0 - } - l := 4 + nlen + alen + slen - if len(b) < l { - return 0, nil, errInvalidAddr - } - data := b[4:] - var name string - var addr []byte - if nlen > 0 { - name = string(data[:nlen]) - data = data[nlen:] - } - if alen > 0 { - addr = data[:alen] - data = data[alen:] - } - return l, &LinkAddr{Name: name, Addr: addr}, nil -} - -// An Inet4Addr represents an internet address for IPv4. -type Inet4Addr struct { - IP [4]byte // IP address -} - -// Family implements the Family method of Addr interface. -func (a *Inet4Addr) Family() int { return syscall.AF_INET } - -func (a *Inet4Addr) lenAndSpace() (int, int) { - return sizeofSockaddrInet, roundup(sizeofSockaddrInet) -} - -func (a *Inet4Addr) marshal(b []byte) (int, error) { - l, ll := a.lenAndSpace() - if len(b) < ll { - return 0, errShortBuffer - } - b[0] = byte(l) - b[1] = syscall.AF_INET - copy(b[4:8], a.IP[:]) - return ll, nil -} - -// An Inet6Addr represents an internet address for IPv6. -type Inet6Addr struct { - IP [16]byte // IP address - ZoneID int // zone identifier -} - -// Family implements the Family method of Addr interface. -func (a *Inet6Addr) Family() int { return syscall.AF_INET6 } - -func (a *Inet6Addr) lenAndSpace() (int, int) { - return sizeofSockaddrInet6, roundup(sizeofSockaddrInet6) -} - -func (a *Inet6Addr) marshal(b []byte) (int, error) { - l, ll := a.lenAndSpace() - if len(b) < ll { - return 0, errShortBuffer - } - b[0] = byte(l) - b[1] = syscall.AF_INET6 - copy(b[8:24], a.IP[:]) - if a.ZoneID > 0 { - nativeEndian.PutUint32(b[24:28], uint32(a.ZoneID)) - } - return ll, nil -} - -// parseInetAddr parses b as an internet address for IPv4 or IPv6. -func parseInetAddr(af int, b []byte) (Addr, error) { - const ( - off4 = 4 // offset of in_addr - off6 = 8 // offset of in6_addr - ) - switch af { - case syscall.AF_INET: - if len(b) < (off4+1) || len(b) < int(b[0]) || b[0] == 0 { - return nil, errInvalidAddr - } - sockAddrLen := int(b[0]) - a := &Inet4Addr{} - n := off4 + 4 - if sockAddrLen < n { - n = sockAddrLen - } - copy(a.IP[:], b[off4:n]) - return a, nil - case syscall.AF_INET6: - if len(b) < (off6+1) || len(b) < int(b[0]) || b[0] == 0 { - return nil, errInvalidAddr - } - sockAddrLen := int(b[0]) - n := off6 + 16 - if sockAddrLen < n { - n = sockAddrLen - } - a := &Inet6Addr{} - if sockAddrLen == sizeofSockaddrInet6 { - a.ZoneID = int(nativeEndian.Uint32(b[24:28])) - } - copy(a.IP[:], b[off6:n]) - if a.IP[0] == 0xfe && a.IP[1]&0xc0 == 0x80 || a.IP[0] == 0xff && (a.IP[1]&0x0f == 0x01 || a.IP[1]&0x0f == 0x02) { - // KAME based IPv6 protocol stack usually - // embeds the interface index in the - // interface-local or link-local address as - // the kernel-internal form. - id := int(bigEndian.Uint16(a.IP[2:4])) - if id != 0 { - a.ZoneID = id - a.IP[2], a.IP[3] = 0, 0 - } - } - return a, nil - default: - return nil, errInvalidAddr - } -} - -// parseKernelInetAddr parses b as an internet address in conventional -// BSD kernel form. -func parseKernelInetAddr(af int, b []byte) (int, Addr, error) { - // The encoding looks similar to the NLRI encoding. - // +----------------------------+ - // | Length (1 octet) | - // +----------------------------+ - // | Address prefix (variable) | - // +----------------------------+ - // - // The differences between the kernel form and the NLRI - // encoding are: - // - // - The length field of the kernel form indicates the prefix - // length in bytes, not in bits - // - // - In the kernel form, zero value of the length field - // doesn't mean 0.0.0.0/0 or ::/0 - // - // - The kernel form appends leading bytes to the prefix field - // to make the tuple to be conformed with - // the routing message boundary - l := int(b[0]) - if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { - // On Darwin, an address in the kernel form is also - // used as a message filler. - if l == 0 || len(b) > roundup(l) { - l = roundup(l) - } - } else { - l = roundup(l) - } - if len(b) < l { - return 0, nil, errInvalidAddr - } - // Don't reorder case expressions. - // The case expressions for IPv6 must come first. - const ( - off4 = 4 // offset of in_addr - off6 = 8 // offset of in6_addr - ) - switch { - case b[0] == sizeofSockaddrInet6: - a := &Inet6Addr{} - copy(a.IP[:], b[off6:off6+16]) - return int(b[0]), a, nil - case af == syscall.AF_INET6: - a := &Inet6Addr{} - if l-1 < off6 { - copy(a.IP[:], b[1:l]) - } else { - copy(a.IP[:], b[l-off6:l]) - } - return int(b[0]), a, nil - case b[0] == sizeofSockaddrInet: - a := &Inet4Addr{} - copy(a.IP[:], b[off4:off4+4]) - return int(b[0]), a, nil - default: // an old fashion, AF_UNSPEC or unknown means AF_INET - a := &Inet4Addr{} - if l-1 < off4 { - copy(a.IP[:], b[1:l]) - } else { - copy(a.IP[:], b[l-off4:l]) - } - return int(b[0]), a, nil - } -} - -// A DefaultAddr represents an address of various operating -// system-specific features. -type DefaultAddr struct { - af int - Raw []byte // raw format of address -} - -// Family implements the Family method of Addr interface. -func (a *DefaultAddr) Family() int { return a.af } - -func (a *DefaultAddr) lenAndSpace() (int, int) { - l := len(a.Raw) - return l, roundup(l) -} - -func (a *DefaultAddr) marshal(b []byte) (int, error) { - l, ll := a.lenAndSpace() - if len(b) < ll { - return 0, errShortBuffer - } - if l > 255 { - return 0, errInvalidAddr - } - b[1] = byte(l) - copy(b[:l], a.Raw) - return ll, nil -} - -func parseDefaultAddr(b []byte) (Addr, error) { - if len(b) < 2 || len(b) < int(b[0]) { - return nil, errInvalidAddr - } - a := &DefaultAddr{af: int(b[1]), Raw: b[:b[0]]} - return a, nil -} - -func addrsSpace(as []Addr) int { - var l int - for _, a := range as { - switch a := a.(type) { - case *LinkAddr: - _, ll := a.lenAndSpace() - l += ll - case *Inet4Addr: - _, ll := a.lenAndSpace() - l += ll - case *Inet6Addr: - _, ll := a.lenAndSpace() - l += ll - case *DefaultAddr: - _, ll := a.lenAndSpace() - l += ll - } - } - return l -} - -// marshalAddrs marshals as and returns a bitmap indicating which -// address is stored in b. -func marshalAddrs(b []byte, as []Addr) (uint, error) { - var attrs uint - for i, a := range as { - switch a := a.(type) { - case *LinkAddr: - l, err := a.marshal(b) - if err != nil { - return 0, err - } - b = b[l:] - attrs |= 1 << uint(i) - case *Inet4Addr: - l, err := a.marshal(b) - if err != nil { - return 0, err - } - b = b[l:] - attrs |= 1 << uint(i) - case *Inet6Addr: - l, err := a.marshal(b) - if err != nil { - return 0, err - } - b = b[l:] - attrs |= 1 << uint(i) - case *DefaultAddr: - l, err := a.marshal(b) - if err != nil { - return 0, err - } - b = b[l:] - attrs |= 1 << uint(i) - } - } - return attrs, nil -} - -func parseAddrs(attrs uint, fn func(int, []byte) (int, Addr, error), b []byte) ([]Addr, error) { - var as [syscall.RTAX_MAX]Addr - af := int(syscall.AF_UNSPEC) - for i := uint(0); i < syscall.RTAX_MAX && len(b) >= roundup(0); i++ { - if attrs&(1< 0 { - af = int(b[1]) - a, err := parseInetAddr(af, b) - if err != nil { - return nil, err - } - as[i] = a - } - l := roundup(int(b[0])) - if len(b) < l { - return nil, errMessageTooShort - } - b = b[l:] - default: - l, a, err := fn(af, b) - if err != nil { - return nil, err - } - as[i] = a - ll := roundup(l) - if len(b) < ll { - b = b[l:] - } else { - b = b[ll:] - } - } - } else { - a, err := parseDefaultAddr(b) - if err != nil { - return nil, err - } - as[i] = a - l := roundup(int(b[0])) - if len(b) < l { - return nil, errMessageTooShort - } - b = b[l:] - } - } - // The only remaining bytes in b should be alignment. - // However, under some circumstances DragonFly BSD appears to put - // more addresses in the message than are indicated in the address - // bitmask, so don't check for this. - return as[:], nil -} diff --git a/src/vendor/golang.org/x/net/route/binary.go b/src/vendor/golang.org/x/net/route/binary.go deleted file mode 100644 index db3f7e0c2a5ed4..00000000000000 --- a/src/vendor/golang.org/x/net/route/binary.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 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. - -//go:build darwin || dragonfly || freebsd || netbsd || openbsd - -package route - -// This file contains duplicates of encoding/binary package. -// -// This package is supposed to be used by the net package of standard -// library. Therefore the package set used in the package must be the -// same as net package. - -var ( - littleEndian binaryLittleEndian - bigEndian binaryBigEndian -) - -type binaryByteOrder interface { - Uint16([]byte) uint16 - Uint32([]byte) uint32 - PutUint16([]byte, uint16) - PutUint32([]byte, uint32) - Uint64([]byte) uint64 -} - -type binaryLittleEndian struct{} - -func (binaryLittleEndian) Uint16(b []byte) uint16 { - _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 - return uint16(b[0]) | uint16(b[1])<<8 -} - -func (binaryLittleEndian) PutUint16(b []byte, v uint16) { - _ = b[1] // early bounds check to guarantee safety of writes below - b[0] = byte(v) - b[1] = byte(v >> 8) -} - -func (binaryLittleEndian) Uint32(b []byte) uint32 { - _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 -} - -func (binaryLittleEndian) PutUint32(b []byte, v uint32) { - _ = b[3] // early bounds check to guarantee safety of writes below - b[0] = byte(v) - b[1] = byte(v >> 8) - b[2] = byte(v >> 16) - b[3] = byte(v >> 24) -} - -func (binaryLittleEndian) Uint64(b []byte) uint64 { - _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | - uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 -} - -type binaryBigEndian struct{} - -func (binaryBigEndian) Uint16(b []byte) uint16 { - _ = b[1] // bounds check hint to compiler; see golang.org/issue/14808 - return uint16(b[1]) | uint16(b[0])<<8 -} - -func (binaryBigEndian) PutUint16(b []byte, v uint16) { - _ = b[1] // early bounds check to guarantee safety of writes below - b[0] = byte(v >> 8) - b[1] = byte(v) -} - -func (binaryBigEndian) Uint32(b []byte) uint32 { - _ = b[3] // bounds check hint to compiler; see golang.org/issue/14808 - return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 -} - -func (binaryBigEndian) PutUint32(b []byte, v uint32) { - _ = b[3] // early bounds check to guarantee safety of writes below - b[0] = byte(v >> 24) - b[1] = byte(v >> 16) - b[2] = byte(v >> 8) - b[3] = byte(v) -} - -func (binaryBigEndian) Uint64(b []byte) uint64 { - _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808 - return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | - uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 -} diff --git a/src/vendor/golang.org/x/net/route/empty.s b/src/vendor/golang.org/x/net/route/empty.s deleted file mode 100644 index 49d79791e01bb6..00000000000000 --- a/src/vendor/golang.org/x/net/route/empty.s +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2018 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. - -//go:build darwin && go1.12 - -// This exists solely so we can linkname in symbols from syscall. diff --git a/src/vendor/golang.org/x/net/route/interface_announce.go b/src/vendor/golang.org/x/net/route/interface_announce.go deleted file mode 100644 index 70614c1b1a17ef..00000000000000 --- a/src/vendor/golang.org/x/net/route/interface_announce.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2016 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. - -//go:build dragonfly || freebsd || netbsd - -package route - -func (w *wireFormat) parseInterfaceAnnounceMessage(_ RIBType, b []byte) (Message, error) { - if len(b) < w.bodyOff { - return nil, errMessageTooShort - } - l := int(nativeEndian.Uint16(b[:2])) - if len(b) < l { - return nil, errInvalidMessage - } - m := &InterfaceAnnounceMessage{ - Version: int(b[2]), - Type: int(b[3]), - Index: int(nativeEndian.Uint16(b[4:6])), - What: int(nativeEndian.Uint16(b[22:24])), - raw: b[:l], - } - for i := 0; i < 16; i++ { - if b[6+i] != 0 { - continue - } - m.Name = string(b[6 : 6+i]) - break - } - return m, nil -} diff --git a/src/vendor/golang.org/x/net/route/route.go b/src/vendor/golang.org/x/net/route/route.go deleted file mode 100644 index ca2ce2b88764cb..00000000000000 --- a/src/vendor/golang.org/x/net/route/route.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2016 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. - -//go:build darwin || dragonfly || freebsd || netbsd || openbsd - -// Package route provides basic functions for the manipulation of -// packet routing facilities on BSD variants. -// -// The package supports any version of Darwin, any version of -// DragonFly BSD, FreeBSD 7 and above, NetBSD 6 and above, and OpenBSD -// 5.6 and above. -package route - -import ( - "errors" - "os" - "syscall" -) - -var ( - errUnsupportedMessage = errors.New("unsupported message") - errMessageMismatch = errors.New("message mismatch") - errMessageTooShort = errors.New("message too short") - errInvalidMessage = errors.New("invalid message") - errInvalidAddr = errors.New("invalid address") - errShortBuffer = errors.New("short buffer") -) - -// A RouteMessage represents a message conveying an address prefix, a -// nexthop address and an output interface. -// -// Unlike other messages, this message can be used to query adjacency -// information for the given address prefix, to add a new route, and -// to delete or modify the existing route from the routing information -// base inside the kernel by writing and reading route messages on a -// routing socket. -// -// For the manipulation of routing information, the route message must -// contain appropriate fields that include: -// -// Version = -// Type = -// Flags = -// Index = -// ID = -// Seq = -// Addrs = -// -// The Type field specifies a type of manipulation, the Flags field -// specifies a class of target information and the Addrs field -// specifies target information like the following: -// -// route.RouteMessage{ -// Version: RTM_VERSION, -// Type: RTM_GET, -// Flags: RTF_UP | RTF_HOST, -// ID: uintptr(os.Getpid()), -// Seq: 1, -// Addrs: []route.Addrs{ -// RTAX_DST: &route.Inet4Addr{ ... }, -// RTAX_IFP: &route.LinkAddr{ ... }, -// RTAX_BRD: &route.Inet4Addr{ ... }, -// }, -// } -// -// The values for the above fields depend on the implementation of -// each operating system. -// -// The Err field on a response message contains an error value on the -// requested operation. If non-nil, the requested operation is failed. -type RouteMessage struct { - Version int // message version - Type int // message type - Flags int // route flags - Index int // interface index when attached - ID uintptr // sender's identifier; usually process ID - Seq int // sequence number - Err error // error on requested operation - Addrs []Addr // addresses - - extOff int // offset of header extension - raw []byte // raw message -} - -// Marshal returns the binary encoding of m. -func (m *RouteMessage) Marshal() ([]byte, error) { - return m.marshal() -} - -// A RIBType represents a type of routing information base. -type RIBType int - -const ( - RIBTypeRoute RIBType = syscall.NET_RT_DUMP - RIBTypeInterface RIBType = syscall.NET_RT_IFLIST -) - -// FetchRIB fetches a routing information base from the operating -// system. -// -// The provided af must be an address family. -// -// The provided arg must be a RIBType-specific argument. -// When RIBType is related to routes, arg might be a set of route -// flags. When RIBType is related to network interfaces, arg might be -// an interface index or a set of interface flags. In most cases, zero -// means a wildcard. -func FetchRIB(af int, typ RIBType, arg int) ([]byte, error) { - try := 0 - for { - try++ - mib := [6]int32{syscall.CTL_NET, syscall.AF_ROUTE, 0, int32(af), int32(typ), int32(arg)} - n := uintptr(0) - if err := sysctl(mib[:], nil, &n, nil, 0); err != nil { - return nil, os.NewSyscallError("sysctl", err) - } - if n == 0 { - return nil, nil - } - b := make([]byte, n) - if err := sysctl(mib[:], &b[0], &n, nil, 0); err != nil { - // If the sysctl failed because the data got larger - // between the two sysctl calls, try a few times - // before failing. (golang.org/issue/45736). - const maxTries = 3 - if err == syscall.ENOMEM && try < maxTries { - continue - } - return nil, os.NewSyscallError("sysctl", err) - } - return b[:n], nil - } -} diff --git a/src/vendor/golang.org/x/net/route/route_classic.go b/src/vendor/golang.org/x/net/route/route_classic.go deleted file mode 100644 index e273fe39ab32e1..00000000000000 --- a/src/vendor/golang.org/x/net/route/route_classic.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2016 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. - -//go:build darwin || dragonfly || freebsd || netbsd - -package route - -import ( - "runtime" - "syscall" -) - -func (m *RouteMessage) marshal() ([]byte, error) { - w, ok := wireFormats[m.Type] - if !ok { - return nil, errUnsupportedMessage - } - l := w.bodyOff + addrsSpace(m.Addrs) - if runtime.GOOS == "darwin" || runtime.GOOS == "ios" { - // Fix stray pointer writes on macOS. - // See golang.org/issue/22456. - l += 1024 - } - b := make([]byte, l) - nativeEndian.PutUint16(b[:2], uint16(l)) - if m.Version == 0 { - b[2] = rtmVersion - } else { - b[2] = byte(m.Version) - } - b[3] = byte(m.Type) - nativeEndian.PutUint32(b[8:12], uint32(m.Flags)) - nativeEndian.PutUint16(b[4:6], uint16(m.Index)) - nativeEndian.PutUint32(b[16:20], uint32(m.ID)) - nativeEndian.PutUint32(b[20:24], uint32(m.Seq)) - attrs, err := marshalAddrs(b[w.bodyOff:], m.Addrs) - if err != nil { - return nil, err - } - if attrs > 0 { - nativeEndian.PutUint32(b[12:16], uint32(attrs)) - } - return b, nil -} - -func (w *wireFormat) parseRouteMessage(typ RIBType, b []byte) (Message, error) { - if len(b) < w.bodyOff { - return nil, errMessageTooShort - } - l := int(nativeEndian.Uint16(b[:2])) - if len(b) < l { - return nil, errInvalidMessage - } - m := &RouteMessage{ - Version: int(b[2]), - Type: int(b[3]), - Flags: int(nativeEndian.Uint32(b[8:12])), - Index: int(nativeEndian.Uint16(b[4:6])), - ID: uintptr(nativeEndian.Uint32(b[16:20])), - Seq: int(nativeEndian.Uint32(b[20:24])), - extOff: w.extOff, - raw: b[:l], - } - errno := syscall.Errno(nativeEndian.Uint32(b[28:32])) - if errno != 0 { - m.Err = errno - } - var err error - m.Addrs, err = parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[w.bodyOff:]) - if err != nil { - return nil, err - } - return m, nil -} diff --git a/src/vendor/golang.org/x/net/route/route_openbsd.go b/src/vendor/golang.org/x/net/route/route_openbsd.go deleted file mode 100644 index f848fb1f2453ae..00000000000000 --- a/src/vendor/golang.org/x/net/route/route_openbsd.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 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. - -package route - -import ( - "syscall" -) - -func (m *RouteMessage) marshal() ([]byte, error) { - l := sizeofRtMsghdr + addrsSpace(m.Addrs) - b := make([]byte, l) - nativeEndian.PutUint16(b[:2], uint16(l)) - if m.Version == 0 { - b[2] = syscall.RTM_VERSION - } else { - b[2] = byte(m.Version) - } - b[3] = byte(m.Type) - nativeEndian.PutUint16(b[4:6], uint16(sizeofRtMsghdr)) - nativeEndian.PutUint32(b[16:20], uint32(m.Flags)) - nativeEndian.PutUint16(b[6:8], uint16(m.Index)) - nativeEndian.PutUint32(b[24:28], uint32(m.ID)) - nativeEndian.PutUint32(b[28:32], uint32(m.Seq)) - attrs, err := marshalAddrs(b[sizeofRtMsghdr:], m.Addrs) - if err != nil { - return nil, err - } - if attrs > 0 { - nativeEndian.PutUint32(b[12:16], uint32(attrs)) - } - return b, nil -} - -func (*wireFormat) parseRouteMessage(_ RIBType, b []byte) (Message, error) { - if len(b) < sizeofRtMsghdr { - return nil, errMessageTooShort - } - l := int(nativeEndian.Uint16(b[:2])) - if len(b) < l { - return nil, errInvalidMessage - } - m := &RouteMessage{ - Version: int(b[2]), - Type: int(b[3]), - Flags: int(nativeEndian.Uint32(b[16:20])), - Index: int(nativeEndian.Uint16(b[6:8])), - ID: uintptr(nativeEndian.Uint32(b[24:28])), - Seq: int(nativeEndian.Uint32(b[28:32])), - raw: b[:l], - } - ll := int(nativeEndian.Uint16(b[4:6])) - if len(b) < ll { - return nil, errInvalidMessage - } - errno := syscall.Errno(nativeEndian.Uint32(b[32:36])) - if errno != 0 { - m.Err = errno - } - as, err := parseAddrs(uint(nativeEndian.Uint32(b[12:16])), parseKernelInetAddr, b[ll:]) - if err != nil { - return nil, err - } - m.Addrs = as - return m, nil -} diff --git a/src/vendor/golang.org/x/net/route/sys_darwin.go b/src/vendor/golang.org/x/net/route/sys_darwin.go deleted file mode 100644 index c8c4eecb8e9c6d..00000000000000 --- a/src/vendor/golang.org/x/net/route/sys_darwin.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2016 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. - -package route - -import "syscall" - -func (typ RIBType) parseable() bool { - switch typ { - case syscall.NET_RT_STAT, syscall.NET_RT_TRASH: - return false - default: - return true - } -} - -// RouteMetrics represents route metrics. -type RouteMetrics struct { - PathMTU int // path maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *RouteMessage) Sys() []Sys { - return []Sys{ - &RouteMetrics{ - PathMTU: int(nativeEndian.Uint32(m.raw[m.extOff+4 : m.extOff+8])), - }, - } -} - -// InterfaceMetrics represents interface metrics. -type InterfaceMetrics struct { - Type int // interface type - MTU int // maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *InterfaceMessage) Sys() []Sys { - return []Sys{ - &InterfaceMetrics{ - Type: int(m.raw[m.extOff]), - MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), - }, - } -} - -func probeRoutingStack() (int, map[int]*wireFormat) { - rtm := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdrDarwin15} - rtm.parse = rtm.parseRouteMessage - rtm2 := &wireFormat{extOff: 36, bodyOff: sizeofRtMsghdr2Darwin15} - rtm2.parse = rtm2.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDarwin15} - ifm.parse = ifm.parseInterfaceMessage - ifm2 := &wireFormat{extOff: 32, bodyOff: sizeofIfMsghdr2Darwin15} - ifm2.parse = ifm2.parseInterfaceMessage - ifam := &wireFormat{extOff: sizeofIfaMsghdrDarwin15, bodyOff: sizeofIfaMsghdrDarwin15} - ifam.parse = ifam.parseInterfaceAddrMessage - ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDarwin15, bodyOff: sizeofIfmaMsghdrDarwin15} - ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage - ifmam2 := &wireFormat{extOff: sizeofIfmaMsghdr2Darwin15, bodyOff: sizeofIfmaMsghdr2Darwin15} - ifmam2.parse = ifmam2.parseInterfaceMulticastAddrMessage - // Darwin kernels require 32-bit aligned access to routing facilities. - return 4, map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_NEWMADDR: ifmam, - syscall.RTM_DELMADDR: ifmam, - syscall.RTM_IFINFO2: ifm2, - syscall.RTM_NEWMADDR2: ifmam2, - syscall.RTM_GET2: rtm2, - } -} diff --git a/src/vendor/golang.org/x/net/route/sys_dragonfly.go b/src/vendor/golang.org/x/net/route/sys_dragonfly.go deleted file mode 100644 index 577fb16eb4bb53..00000000000000 --- a/src/vendor/golang.org/x/net/route/sys_dragonfly.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2016 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. - -package route - -import ( - "syscall" - "unsafe" -) - -func (typ RIBType) parseable() bool { return true } - -// RouteMetrics represents route metrics. -type RouteMetrics struct { - PathMTU int // path maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *RouteMessage) Sys() []Sys { - return []Sys{ - &RouteMetrics{ - PathMTU: int(nativeEndian.Uint64(m.raw[m.extOff+8 : m.extOff+16])), - }, - } -} - -// InterfaceMetrics represents interface metrics. -type InterfaceMetrics struct { - Type int // interface type - MTU int // maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *InterfaceMessage) Sys() []Sys { - return []Sys{ - &InterfaceMetrics{ - Type: int(m.raw[m.extOff]), - MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), - }, - } -} - -func probeRoutingStack() (int, map[int]*wireFormat) { - var p uintptr - rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrDragonFlyBSD4} - rtm.parse = rtm.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrDragonFlyBSD4} - ifm.parse = ifm.parseInterfaceMessage - ifam := &wireFormat{extOff: sizeofIfaMsghdrDragonFlyBSD4, bodyOff: sizeofIfaMsghdrDragonFlyBSD4} - ifam.parse = ifam.parseInterfaceAddrMessage - ifmam := &wireFormat{extOff: sizeofIfmaMsghdrDragonFlyBSD4, bodyOff: sizeofIfmaMsghdrDragonFlyBSD4} - ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage - ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrDragonFlyBSD4, bodyOff: sizeofIfAnnouncemsghdrDragonFlyBSD4} - ifanm.parse = ifanm.parseInterfaceAnnounceMessage - - rel, _ := syscall.SysctlUint32("kern.osreldate") - if rel >= 500705 { - // https://github.com/DragonFlyBSD/DragonFlyBSD/commit/43a373152df2d405c9940983e584e6a25e76632d - // but only the size of struct ifa_msghdr actually changed - rtmVersion = 7 - ifam.bodyOff = sizeofIfaMsghdrDragonFlyBSD58 - } - - return int(unsafe.Sizeof(p)), map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_NEWMADDR: ifmam, - syscall.RTM_DELMADDR: ifmam, - syscall.RTM_IFANNOUNCE: ifanm, - } -} diff --git a/src/vendor/golang.org/x/net/route/sys_freebsd.go b/src/vendor/golang.org/x/net/route/sys_freebsd.go deleted file mode 100644 index 0a66dcedb386d9..00000000000000 --- a/src/vendor/golang.org/x/net/route/sys_freebsd.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2016 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. - -package route - -import ( - "syscall" - "unsafe" -) - -func (typ RIBType) parseable() bool { return true } - -// RouteMetrics represents route metrics. -type RouteMetrics struct { - PathMTU int // path maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *RouteMessage) Sys() []Sys { - if kernelAlign == 8 { - return []Sys{ - &RouteMetrics{ - PathMTU: int(nativeEndian.Uint64(m.raw[m.extOff+8 : m.extOff+16])), - }, - } - } - return []Sys{ - &RouteMetrics{ - PathMTU: int(nativeEndian.Uint32(m.raw[m.extOff+4 : m.extOff+8])), - }, - } -} - -// InterfaceMetrics represents interface metrics. -type InterfaceMetrics struct { - Type int // interface type - MTU int // maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *InterfaceMessage) Sys() []Sys { - return []Sys{ - &InterfaceMetrics{ - Type: int(m.raw[m.extOff]), - MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), - }, - } -} - -var compatFreeBSD32 bool // 386 emulation on amd64 - -func probeRoutingStack() (int, map[int]*wireFormat) { - var p uintptr - wordSize := int(unsafe.Sizeof(p)) - align := wordSize - // In the case of kern.supported_archs="amd64 i386", we need - // to know the underlying kernel's architecture because the - // alignment for routing facilities are set at the build time - // of the kernel. - conf, _ := syscall.Sysctl("kern.conftxt") - for i, j := 0, 0; j < len(conf); j++ { - if conf[j] != '\n' { - continue - } - s := conf[i:j] - i = j + 1 - if len(s) > len("machine") && s[:len("machine")] == "machine" { - s = s[len("machine"):] - for k := 0; k < len(s); k++ { - if s[k] == ' ' || s[k] == '\t' { - s = s[1:] - } - break - } - if s == "amd64" { - align = 8 - } - break - } - } - if align != wordSize { - compatFreeBSD32 = true // 386 emulation on amd64 - } - var rtm, ifm, ifam, ifmam, ifanm *wireFormat - if compatFreeBSD32 { - rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10Emu - sizeofRtMetricsFreeBSD10Emu, bodyOff: sizeofRtMsghdrFreeBSD10Emu} - ifm = &wireFormat{extOff: 16} - ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10Emu, bodyOff: sizeofIfaMsghdrFreeBSD10Emu} - ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10Emu, bodyOff: sizeofIfmaMsghdrFreeBSD10Emu} - ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10Emu, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10Emu} - } else { - rtm = &wireFormat{extOff: sizeofRtMsghdrFreeBSD10 - sizeofRtMetricsFreeBSD10, bodyOff: sizeofRtMsghdrFreeBSD10} - ifm = &wireFormat{extOff: 16} - ifam = &wireFormat{extOff: sizeofIfaMsghdrFreeBSD10, bodyOff: sizeofIfaMsghdrFreeBSD10} - ifmam = &wireFormat{extOff: sizeofIfmaMsghdrFreeBSD10, bodyOff: sizeofIfmaMsghdrFreeBSD10} - ifanm = &wireFormat{extOff: sizeofIfAnnouncemsghdrFreeBSD10, bodyOff: sizeofIfAnnouncemsghdrFreeBSD10} - } - rel, _ := syscall.SysctlUint32("kern.osreldate") - switch { - case rel < 800000: - if compatFreeBSD32 { - ifm.bodyOff = sizeofIfMsghdrFreeBSD7Emu - } else { - ifm.bodyOff = sizeofIfMsghdrFreeBSD7 - } - case 800000 <= rel && rel < 900000: - if compatFreeBSD32 { - ifm.bodyOff = sizeofIfMsghdrFreeBSD8Emu - } else { - ifm.bodyOff = sizeofIfMsghdrFreeBSD8 - } - case 900000 <= rel && rel < 1000000: - if compatFreeBSD32 { - ifm.bodyOff = sizeofIfMsghdrFreeBSD9Emu - } else { - ifm.bodyOff = sizeofIfMsghdrFreeBSD9 - } - case 1000000 <= rel && rel < 1100000: - if compatFreeBSD32 { - ifm.bodyOff = sizeofIfMsghdrFreeBSD10Emu - } else { - ifm.bodyOff = sizeofIfMsghdrFreeBSD10 - } - default: - if compatFreeBSD32 { - ifm.bodyOff = sizeofIfMsghdrFreeBSD11Emu - } else { - ifm.bodyOff = sizeofIfMsghdrFreeBSD11 - } - } - rtm.parse = rtm.parseRouteMessage - ifm.parse = ifm.parseInterfaceMessage - ifam.parse = ifam.parseInterfaceAddrMessage - ifmam.parse = ifmam.parseInterfaceMulticastAddrMessage - ifanm.parse = ifanm.parseInterfaceAnnounceMessage - return align, map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_NEWMADDR: ifmam, - syscall.RTM_DELMADDR: ifmam, - syscall.RTM_IFANNOUNCE: ifanm, - } -} diff --git a/src/vendor/golang.org/x/net/route/sys_netbsd.go b/src/vendor/golang.org/x/net/route/sys_netbsd.go deleted file mode 100644 index be4460e13f044c..00000000000000 --- a/src/vendor/golang.org/x/net/route/sys_netbsd.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2016 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. - -package route - -import "syscall" - -func (typ RIBType) parseable() bool { return true } - -// RouteMetrics represents route metrics. -type RouteMetrics struct { - PathMTU int // path maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *RouteMessage) Sys() []Sys { - return []Sys{ - &RouteMetrics{ - PathMTU: int(nativeEndian.Uint64(m.raw[m.extOff+8 : m.extOff+16])), - }, - } -} - -// RouteMetrics represents route metrics. -type InterfaceMetrics struct { - Type int // interface type - MTU int // maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *InterfaceMessage) Sys() []Sys { - return []Sys{ - &InterfaceMetrics{ - Type: int(m.raw[m.extOff]), - MTU: int(nativeEndian.Uint32(m.raw[m.extOff+8 : m.extOff+12])), - }, - } -} - -func probeRoutingStack() (int, map[int]*wireFormat) { - rtm := &wireFormat{extOff: 40, bodyOff: sizeofRtMsghdrNetBSD7} - rtm.parse = rtm.parseRouteMessage - ifm := &wireFormat{extOff: 16, bodyOff: sizeofIfMsghdrNetBSD7} - ifm.parse = ifm.parseInterfaceMessage - ifam := &wireFormat{extOff: sizeofIfaMsghdrNetBSD7, bodyOff: sizeofIfaMsghdrNetBSD7} - ifam.parse = ifam.parseInterfaceAddrMessage - ifanm := &wireFormat{extOff: sizeofIfAnnouncemsghdrNetBSD7, bodyOff: sizeofIfAnnouncemsghdrNetBSD7} - ifanm.parse = ifanm.parseInterfaceAnnounceMessage - // NetBSD 6 and above kernels require 64-bit aligned access to - // routing facilities. - return 8, map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_LOCK: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFANNOUNCE: ifanm, - syscall.RTM_IFINFO: ifm, - } -} diff --git a/src/vendor/golang.org/x/net/route/sys_openbsd.go b/src/vendor/golang.org/x/net/route/sys_openbsd.go deleted file mode 100644 index 7f4f93cbea088d..00000000000000 --- a/src/vendor/golang.org/x/net/route/sys_openbsd.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2016 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. - -package route - -import ( - "syscall" - "unsafe" -) - -func (typ RIBType) parseable() bool { - switch typ { - case syscall.NET_RT_STATS, syscall.NET_RT_TABLE: - return false - default: - return true - } -} - -// RouteMetrics represents route metrics. -type RouteMetrics struct { - PathMTU int // path maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (rmx *RouteMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *RouteMessage) Sys() []Sys { - return []Sys{ - &RouteMetrics{ - PathMTU: int(nativeEndian.Uint32(m.raw[60:64])), - }, - } -} - -// InterfaceMetrics represents interface metrics. -type InterfaceMetrics struct { - Type int // interface type - MTU int // maximum transmission unit -} - -// SysType implements the SysType method of Sys interface. -func (imx *InterfaceMetrics) SysType() SysType { return SysMetrics } - -// Sys implements the Sys method of Message interface. -func (m *InterfaceMessage) Sys() []Sys { - return []Sys{ - &InterfaceMetrics{ - Type: int(m.raw[24]), - MTU: int(nativeEndian.Uint32(m.raw[28:32])), - }, - } -} - -func probeRoutingStack() (int, map[int]*wireFormat) { - var p uintptr - rtm := &wireFormat{extOff: -1, bodyOff: -1} - rtm.parse = rtm.parseRouteMessage - ifm := &wireFormat{extOff: -1, bodyOff: -1} - ifm.parse = ifm.parseInterfaceMessage - ifam := &wireFormat{extOff: -1, bodyOff: -1} - ifam.parse = ifam.parseInterfaceAddrMessage - ifanm := &wireFormat{extOff: -1, bodyOff: -1} - ifanm.parse = ifanm.parseInterfaceAnnounceMessage - return int(unsafe.Sizeof(p)), map[int]*wireFormat{ - syscall.RTM_ADD: rtm, - syscall.RTM_DELETE: rtm, - syscall.RTM_CHANGE: rtm, - syscall.RTM_GET: rtm, - syscall.RTM_LOSING: rtm, - syscall.RTM_REDIRECT: rtm, - syscall.RTM_MISS: rtm, - syscall.RTM_RESOLVE: rtm, - syscall.RTM_NEWADDR: ifam, - syscall.RTM_DELADDR: ifam, - syscall.RTM_IFINFO: ifm, - syscall.RTM_IFANNOUNCE: ifanm, - syscall.RTM_DESYNC: rtm, - } -} diff --git a/src/vendor/golang.org/x/net/route/syscall.go b/src/vendor/golang.org/x/net/route/syscall.go deleted file mode 100644 index 0ed53750a22f8a..00000000000000 --- a/src/vendor/golang.org/x/net/route/syscall.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2016 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. - -//go:build darwin || dragonfly || freebsd || netbsd || openbsd - -package route - -import _ "unsafe" // for linkname - -//go:linkname sysctl syscall.sysctl -func sysctl(mib []int32, old *byte, oldlen *uintptr, new *byte, newlen uintptr) error diff --git a/src/vendor/golang.org/x/net/route/zsys_darwin.go b/src/vendor/golang.org/x/net/route/zsys_darwin.go deleted file mode 100644 index 56a0c66f44f202..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_darwin.go +++ /dev/null @@ -1,22 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_darwin.go - -package route - -const ( - sizeofIfMsghdrDarwin15 = 0x70 - sizeofIfaMsghdrDarwin15 = 0x14 - sizeofIfmaMsghdrDarwin15 = 0x10 - sizeofIfMsghdr2Darwin15 = 0xa0 - sizeofIfmaMsghdr2Darwin15 = 0x14 - sizeofIfDataDarwin15 = 0x60 - sizeofIfData64Darwin15 = 0x80 - - sizeofRtMsghdrDarwin15 = 0x5c - sizeofRtMsghdr2Darwin15 = 0x5c - sizeofRtMetricsDarwin15 = 0x38 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_dragonfly.go b/src/vendor/golang.org/x/net/route/zsys_dragonfly.go deleted file mode 100644 index f7c7a60cd64220..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_dragonfly.go +++ /dev/null @@ -1,20 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_dragonfly.go - -package route - -const ( - sizeofIfMsghdrDragonFlyBSD4 = 0xb0 - sizeofIfaMsghdrDragonFlyBSD4 = 0x14 - sizeofIfmaMsghdrDragonFlyBSD4 = 0x10 - sizeofIfAnnouncemsghdrDragonFlyBSD4 = 0x18 - - sizeofIfaMsghdrDragonFlyBSD58 = 0x18 - - sizeofRtMsghdrDragonFlyBSD4 = 0x98 - sizeofRtMetricsDragonFlyBSD4 = 0x70 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go deleted file mode 100644 index 3f985c7ee90c1d..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_386.go +++ /dev/null @@ -1,55 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_freebsd.go - -package route - -const ( - sizeofIfMsghdrlFreeBSD10 = 0x68 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0x6c - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - - sizeofRtMsghdrFreeBSD10 = 0x5c - sizeofRtMetricsFreeBSD10 = 0x38 - - sizeofIfMsghdrFreeBSD7 = 0x60 - sizeofIfMsghdrFreeBSD8 = 0x60 - sizeofIfMsghdrFreeBSD9 = 0x60 - sizeofIfMsghdrFreeBSD10 = 0x64 - sizeofIfMsghdrFreeBSD11 = 0xa8 - - sizeofIfDataFreeBSD7 = 0x50 - sizeofIfDataFreeBSD8 = 0x50 - sizeofIfDataFreeBSD9 = 0x50 - sizeofIfDataFreeBSD10 = 0x54 - sizeofIfDataFreeBSD11 = 0x98 - - // MODIFIED BY HAND FOR 386 EMULATION ON AMD64 - // 386 EMULATION USES THE UNDERLYING RAW DATA LAYOUT - - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - - sizeofRtMsghdrFreeBSD10Emu = 0x98 - sizeofRtMetricsFreeBSD10Emu = 0x70 - - sizeofIfMsghdrFreeBSD7Emu = 0xa8 - sizeofIfMsghdrFreeBSD8Emu = 0xa8 - sizeofIfMsghdrFreeBSD9Emu = 0xa8 - sizeofIfMsghdrFreeBSD10Emu = 0xa8 - sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go deleted file mode 100644 index 929339369829dc..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_amd64.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_freebsd.go - -package route - -const ( - sizeofIfMsghdrlFreeBSD10 = 0xb0 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - - sizeofRtMsghdrFreeBSD10 = 0x98 - sizeofRtMetricsFreeBSD10 = 0x70 - - sizeofIfMsghdrFreeBSD7 = 0xa8 - sizeofIfMsghdrFreeBSD8 = 0xa8 - sizeofIfMsghdrFreeBSD9 = 0xa8 - sizeofIfMsghdrFreeBSD10 = 0xa8 - sizeofIfMsghdrFreeBSD11 = 0xa8 - - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - - sizeofRtMsghdrFreeBSD10Emu = 0x98 - sizeofRtMetricsFreeBSD10Emu = 0x70 - - sizeofIfMsghdrFreeBSD7Emu = 0xa8 - sizeofIfMsghdrFreeBSD8Emu = 0xa8 - sizeofIfMsghdrFreeBSD9Emu = 0xa8 - sizeofIfMsghdrFreeBSD10Emu = 0xa8 - sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go deleted file mode 100644 index a2bdb4ad3b58ea..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_freebsd.go - -package route - -const ( - sizeofIfMsghdrlFreeBSD10 = 0x68 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0x6c - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - - sizeofRtMsghdrFreeBSD10 = 0x5c - sizeofRtMetricsFreeBSD10 = 0x38 - - sizeofIfMsghdrFreeBSD7 = 0x70 - sizeofIfMsghdrFreeBSD8 = 0x70 - sizeofIfMsghdrFreeBSD9 = 0x70 - sizeofIfMsghdrFreeBSD10 = 0x70 - sizeofIfMsghdrFreeBSD11 = 0xa8 - - sizeofIfDataFreeBSD7 = 0x60 - sizeofIfDataFreeBSD8 = 0x60 - sizeofIfDataFreeBSD9 = 0x60 - sizeofIfDataFreeBSD10 = 0x60 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0x68 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0x6c - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - - sizeofRtMsghdrFreeBSD10Emu = 0x5c - sizeofRtMetricsFreeBSD10Emu = 0x38 - - sizeofIfMsghdrFreeBSD7Emu = 0x70 - sizeofIfMsghdrFreeBSD8Emu = 0x70 - sizeofIfMsghdrFreeBSD9Emu = 0x70 - sizeofIfMsghdrFreeBSD10Emu = 0x70 - sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x60 - sizeofIfDataFreeBSD8Emu = 0x60 - sizeofIfDataFreeBSD9Emu = 0x60 - sizeofIfDataFreeBSD10Emu = 0x60 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm64.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_arm64.go deleted file mode 100644 index 929339369829dc..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_arm64.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_freebsd.go - -package route - -const ( - sizeofIfMsghdrlFreeBSD10 = 0xb0 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - - sizeofRtMsghdrFreeBSD10 = 0x98 - sizeofRtMetricsFreeBSD10 = 0x70 - - sizeofIfMsghdrFreeBSD7 = 0xa8 - sizeofIfMsghdrFreeBSD8 = 0xa8 - sizeofIfMsghdrFreeBSD9 = 0xa8 - sizeofIfMsghdrFreeBSD10 = 0xa8 - sizeofIfMsghdrFreeBSD11 = 0xa8 - - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - - sizeofRtMsghdrFreeBSD10Emu = 0x98 - sizeofRtMetricsFreeBSD10Emu = 0x70 - - sizeofIfMsghdrFreeBSD7Emu = 0xa8 - sizeofIfMsghdrFreeBSD8Emu = 0xa8 - sizeofIfMsghdrFreeBSD9Emu = 0xa8 - sizeofIfMsghdrFreeBSD10Emu = 0xa8 - sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_freebsd_riscv64.go b/src/vendor/golang.org/x/net/route/zsys_freebsd_riscv64.go deleted file mode 100644 index 929339369829dc..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_freebsd_riscv64.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_freebsd.go - -package route - -const ( - sizeofIfMsghdrlFreeBSD10 = 0xb0 - sizeofIfaMsghdrFreeBSD10 = 0x14 - sizeofIfaMsghdrlFreeBSD10 = 0xb0 - sizeofIfmaMsghdrFreeBSD10 = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10 = 0x18 - - sizeofRtMsghdrFreeBSD10 = 0x98 - sizeofRtMetricsFreeBSD10 = 0x70 - - sizeofIfMsghdrFreeBSD7 = 0xa8 - sizeofIfMsghdrFreeBSD8 = 0xa8 - sizeofIfMsghdrFreeBSD9 = 0xa8 - sizeofIfMsghdrFreeBSD10 = 0xa8 - sizeofIfMsghdrFreeBSD11 = 0xa8 - - sizeofIfDataFreeBSD7 = 0x98 - sizeofIfDataFreeBSD8 = 0x98 - sizeofIfDataFreeBSD9 = 0x98 - sizeofIfDataFreeBSD10 = 0x98 - sizeofIfDataFreeBSD11 = 0x98 - - sizeofIfMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfaMsghdrFreeBSD10Emu = 0x14 - sizeofIfaMsghdrlFreeBSD10Emu = 0xb0 - sizeofIfmaMsghdrFreeBSD10Emu = 0x10 - sizeofIfAnnouncemsghdrFreeBSD10Emu = 0x18 - - sizeofRtMsghdrFreeBSD10Emu = 0x98 - sizeofRtMetricsFreeBSD10Emu = 0x70 - - sizeofIfMsghdrFreeBSD7Emu = 0xa8 - sizeofIfMsghdrFreeBSD8Emu = 0xa8 - sizeofIfMsghdrFreeBSD9Emu = 0xa8 - sizeofIfMsghdrFreeBSD10Emu = 0xa8 - sizeofIfMsghdrFreeBSD11Emu = 0xa8 - - sizeofIfDataFreeBSD7Emu = 0x98 - sizeofIfDataFreeBSD8Emu = 0x98 - sizeofIfDataFreeBSD9Emu = 0x98 - sizeofIfDataFreeBSD10Emu = 0x98 - sizeofIfDataFreeBSD11Emu = 0x98 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_netbsd.go b/src/vendor/golang.org/x/net/route/zsys_netbsd.go deleted file mode 100644 index eaffe8c40804cb..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_netbsd.go +++ /dev/null @@ -1,17 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_netbsd.go - -package route - -const ( - sizeofIfMsghdrNetBSD7 = 0x98 - sizeofIfaMsghdrNetBSD7 = 0x18 - sizeofIfAnnouncemsghdrNetBSD7 = 0x18 - - sizeofRtMsghdrNetBSD7 = 0x78 - sizeofRtMetricsNetBSD7 = 0x50 - - sizeofSockaddrStorage = 0x80 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/net/route/zsys_openbsd.go b/src/vendor/golang.org/x/net/route/zsys_openbsd.go deleted file mode 100644 index b11b8126801b68..00000000000000 --- a/src/vendor/golang.org/x/net/route/zsys_openbsd.go +++ /dev/null @@ -1,12 +0,0 @@ -// Code generated by cmd/cgo -godefs; DO NOT EDIT. -// cgo -godefs defs_openbsd.go - -package route - -const ( - sizeofRtMsghdr = 0x60 - - sizeofSockaddrStorage = 0x100 - sizeofSockaddrInet = 0x10 - sizeofSockaddrInet6 = 0x1c -) diff --git a/src/vendor/golang.org/x/sys/cpu/cpu.go b/src/vendor/golang.org/x/sys/cpu/cpu.go index 02609d5b21d56a..9c105f23afcdc4 100644 --- a/src/vendor/golang.org/x/sys/cpu/cpu.go +++ b/src/vendor/golang.org/x/sys/cpu/cpu.go @@ -72,6 +72,9 @@ var X86 struct { HasSSSE3 bool // Supplemental streaming SIMD extension 3 HasSSE41 bool // Streaming SIMD extension 4 and 4.1 HasSSE42 bool // Streaming SIMD extension 4 and 4.2 + HasAVXIFMA bool // Advanced vector extension Integer Fused Multiply Add + HasAVXVNNI bool // Advanced vector extension Vector Neural Network Instructions + HasAVXVNNIInt8 bool // Advanced vector extension Vector Neural Network Int8 instructions _ CacheLinePad } diff --git a/src/vendor/golang.org/x/sys/cpu/cpu_x86.go b/src/vendor/golang.org/x/sys/cpu/cpu_x86.go index 600a6807861e7f..1e642f3304fa87 100644 --- a/src/vendor/golang.org/x/sys/cpu/cpu_x86.go +++ b/src/vendor/golang.org/x/sys/cpu/cpu_x86.go @@ -53,6 +53,9 @@ func initOptions() { {Name: "sse41", Feature: &X86.HasSSE41}, {Name: "sse42", Feature: &X86.HasSSE42}, {Name: "ssse3", Feature: &X86.HasSSSE3}, + {Name: "avxifma", Feature: &X86.HasAVXIFMA}, + {Name: "avxvnni", Feature: &X86.HasAVXVNNI}, + {Name: "avxvnniint8", Feature: &X86.HasAVXVNNIInt8}, // These capabilities should always be enabled on amd64: {Name: "sse2", Feature: &X86.HasSSE2, Required: runtime.GOARCH == "amd64"}, @@ -106,7 +109,7 @@ func archInit() { return } - _, ebx7, ecx7, edx7 := cpuid(7, 0) + eax7, ebx7, ecx7, edx7 := cpuid(7, 0) X86.HasBMI1 = isSet(3, ebx7) X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX X86.HasBMI2 = isSet(8, ebx7) @@ -134,14 +137,24 @@ func archInit() { X86.HasAVX512VAES = isSet(9, ecx7) X86.HasAVX512VBMI2 = isSet(6, ecx7) X86.HasAVX512BITALG = isSet(12, ecx7) - - eax71, _, _, _ := cpuid(7, 1) - X86.HasAVX512BF16 = isSet(5, eax71) } X86.HasAMXTile = isSet(24, edx7) X86.HasAMXInt8 = isSet(25, edx7) X86.HasAMXBF16 = isSet(22, edx7) + + // These features depend on the second level of extended features. + if eax7 >= 1 { + eax71, _, _, edx71 := cpuid(7, 1) + if X86.HasAVX512 { + X86.HasAVX512BF16 = isSet(5, eax71) + } + if X86.HasAVX { + X86.HasAVXIFMA = isSet(23, eax71) + X86.HasAVXVNNI = isSet(4, eax71) + X86.HasAVXVNNIInt8 = isSet(4, edx71) + } + } } func isSet(bitpos uint, value uint32) bool { diff --git a/src/vendor/modules.txt b/src/vendor/modules.txt index 1c8de570cc2f1f..791a7d8e874bae 100644 --- a/src/vendor/modules.txt +++ b/src/vendor/modules.txt @@ -1,4 +1,4 @@ -# golang.org/x/crypto v0.30.0 +# golang.org/x/crypto v0.33.1-0.20250210163342-e47973b1c108 ## explicit; go 1.20 golang.org/x/crypto/chacha20 golang.org/x/crypto/chacha20poly1305 @@ -6,7 +6,7 @@ golang.org/x/crypto/cryptobyte golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/internal/alias golang.org/x/crypto/internal/poly1305 -# golang.org/x/net v0.32.1-0.20241206180132-552d8ac903a1 +# golang.org/x/net v0.35.1-0.20250213222735-884432780bfd ## explicit; go 1.18 golang.org/x/net/dns/dnsmessage golang.org/x/net/http/httpguts @@ -15,11 +15,10 @@ golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/lif golang.org/x/net/nettest -golang.org/x/net/route -# golang.org/x/sys v0.28.0 +# golang.org/x/sys v0.30.0 ## explicit; go 1.18 golang.org/x/sys/cpu -# golang.org/x/text v0.21.0 +# golang.org/x/text v0.22.0 ## explicit; go 1.18 golang.org/x/text/secure/bidirule golang.org/x/text/transform diff --git a/src/weak/pointer.go b/src/weak/pointer.go index fb10bc2d699ab3..e9d7420880a7f9 100644 --- a/src/weak/pointer.go +++ b/src/weak/pointer.go @@ -13,9 +13,9 @@ import ( // Pointer is a weak pointer to a value of type T. // // Just like regular pointers, Pointer may reference any part of an -// object, such as the field of a struct or an element of an array. +// object, such as a field of a struct or an element of an array. // Objects that are only pointed to by weak pointers are not considered -// reachable and once the object becomes unreachable [Pointer.Value] +// reachable, and once the object becomes unreachable, [Pointer.Value] // may return nil. // // The primary use-cases for weak pointers are for implementing caches, @@ -23,19 +23,19 @@ import ( // the lifetimes of separate values (for example, through a map with weak // keys). // -// Two Pointer values always compare equal if the pointers that they were -// created from compare equal. This property is retained even after the +// Two Pointer values always compare equal if the pointers from which they were +// created compare equal. This property is retained even after the // object referenced by the pointer used to create a weak reference is // reclaimed. -// If multiple weak pointers are made to different offsets within same object +// If multiple weak pointers are made to different offsets within the same object // (for example, pointers to different fields of the same struct), those pointers // will not compare equal. // If a weak pointer is created from an object that becomes unreachable, but is // then resurrected due to a finalizer, that weak pointer will not compare equal -// with weak pointers created after resurrection. +// with weak pointers created after the resurrection. // // Calling [Make] with a nil pointer returns a weak pointer whose [Pointer.Value] -// always returns nil. The zero value of a Pointer behaves as if it was created +// always returns nil. The zero value of a Pointer behaves as if it were created // by passing nil to [Make] and compares equal with such pointers. // // [Pointer.Value] is not guaranteed to eventually return nil. @@ -52,10 +52,13 @@ import ( // nil, even after an object is no longer referenced, the runtime is allowed to // perform a space-saving optimization that batches objects together in a single // allocation slot. The weak pointer for an unreferenced object in such an -// allocation may never be called if it always exists in the same batch as a +// allocation may never become nil if it always exists in the same batch as a // referenced object. Typically, this batching only happens for tiny // (on the order of 16 bytes or less) and pointer-free objects. type Pointer[T any] struct { + // Mention T in the type definition to prevent conversions + // between Pointer types, like we do for sync/atomic.Pointer. + _ [0]*T u unsafe.Pointer } @@ -69,7 +72,7 @@ func Make[T any](ptr *T) Pointer[T] { u = runtime_registerWeakPointer(unsafe.Pointer(ptr)) } runtime.KeepAlive(ptr) - return Pointer[T]{u} + return Pointer[T]{u: u} } // Value returns the original pointer used to create the weak pointer. @@ -78,6 +81,9 @@ func Make[T any](ptr *T) Pointer[T] { // If a weak pointer points to an object with a finalizer, then Value will // return nil as soon as the object's finalizer is queued for execution. func (p Pointer[T]) Value() *T { + if p.u == nil { + return nil + } return (*T)(runtime_makeStrongFromWeak(p.u)) } diff --git a/src/weak/pointer_test.go b/src/weak/pointer_test.go index 002b4130f0b553..70c743381cacf8 100644 --- a/src/weak/pointer_test.go +++ b/src/weak/pointer_test.go @@ -6,10 +6,12 @@ package weak_test import ( "context" + "internal/goarch" "runtime" "sync" "testing" "time" + "unsafe" "weak" ) @@ -21,6 +23,15 @@ type T struct { } func TestPointer(t *testing.T) { + var zero weak.Pointer[T] + if zero.Value() != nil { + t.Error("Value of zero value of weak.Pointer is not nil") + } + zeroNil := weak.Make[T](nil) + if zeroNil.Value() != nil { + t.Error("Value of weak.Make[T](nil) is not nil") + } + bt := new(T) wt := weak.Make(bt) if st := wt.Value(); st != bt { @@ -41,6 +52,12 @@ func TestPointer(t *testing.T) { } func TestPointerEquality(t *testing.T) { + var zero weak.Pointer[T] + zeroNil := weak.Make[T](nil) + if zero != zeroNil { + t.Error("weak.Make[T](nil) != zero value of weak.Pointer[T]") + } + bt := make([]*T, 10) wt := make([]weak.Pointer[T], 10) wo := make([]weak.Pointer[int], 10) @@ -140,6 +157,14 @@ func TestPointerFinalizer(t *testing.T) { } } +func TestPointerSize(t *testing.T) { + var p weak.Pointer[T] + size := unsafe.Sizeof(p) + if size != goarch.PtrSize { + t.Errorf("weak.Pointer[T] size = %d, want %d", size, goarch.PtrSize) + } +} + // Regression test for issue 69210. // // Weak-to-strong conversions must shade the new strong pointer, otherwise diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go index 4b47f6c13d86de..063055053ec95e 100644 --- a/test/codegen/arithmetic.go +++ b/test/codegen/arithmetic.go @@ -185,6 +185,15 @@ func Pow2Muls(n1, n2 int) (int, int) { return a, b } +func Mul_2(n1 int32, n2 int64) (int32, int64) { + // amd64:"ADDL", -"SHLL" + a := n1 * 2 + // amd64:"ADDQ", -"SHLQ" + b := n2 * 2 + + return a, b +} + func Mul_96(n int) int { // amd64:`SHLQ\t[$]5`,`LEAQ\t\(.*\)\(.*\*2\),`,-`IMULQ` // 386:`SHLL\t[$]5`,`LEAL\t\(.*\)\(.*\*2\),`,-`IMULL` diff --git a/test/codegen/bits.go b/test/codegen/bits.go index 354dbf407a4bac..c20e4d67333f63 100644 --- a/test/codegen/bits.go +++ b/test/codegen/bits.go @@ -120,6 +120,16 @@ func bitoff64(a, b uint64) (n uint64) { return n } +func clearLastBit(x int64, y int32) (int64, int32) { + // amd64:"ANDQ\t[$]-2" + a := (x >> 1) << 1 + + // amd64:"ANDL\t[$]-2" + b := (y >> 1) << 1 + + return a, b +} + func bitcompl64(a, b uint64) (n uint64) { // amd64:"BTCQ" n += b ^ (1 << (a & 63)) diff --git a/test/codegen/bool.go b/test/codegen/bool.go index 164ca1b2246aa3..2024759a5c530f 100644 --- a/test/codegen/bool.go +++ b/test/codegen/bool.go @@ -47,6 +47,7 @@ func convertNeqBool32(x uint32) bool { func convertEqBool32(x uint32) bool { // ppc64x:"RLDICL",-"CMPW","XOR",-"ISEL" + // amd64:"ANDL","XORL",-"BTL",-"SETCC" return x&1 == 0 } @@ -57,6 +58,7 @@ func convertNeqBool64(x uint64) bool { func convertEqBool64(x uint64) bool { // ppc64x:"RLDICL","XOR",-"CMP",-"ISEL" + // amd64:"ANDL","XORL",-"BTL",-"SETCC" return x&1 == 0 } diff --git a/test/codegen/comparisons.go b/test/codegen/comparisons.go index 5fbb31c00c8cd8..702ea275cc9b39 100644 --- a/test/codegen/comparisons.go +++ b/test/codegen/comparisons.go @@ -730,6 +730,54 @@ func cmpToCmnLessThan(a, b, c, d int) int { return c1 + c2 + c3 + c4 } +func less128Signed32(x int32) bool { + // amd64:`CMPL.*127` + // amd64:`SETLE` + return x < 128 +} + +func less128Signed64(x int64) bool { + // amd64:`CMPQ.*127` + // amd64:`SETLE` + return x < 128 +} + +func less128Unsigned32(x uint32) bool { + // amd64:`CMPL.*127` + // amd64:`SETLS` + return x < 128 +} + +func less128Unsigned64(x uint64) bool { + // amd64:`CMPQ.*127` + // amd64:`SETLS` + return x < 128 +} + +func ge128Unsigned32(x uint32) bool { + // amd64:`CMPL.*127` + // amd64:`SETHI` + return x >= 128 +} + +func ge128Unsigned64(x uint64) bool { + // amd64:`CMPQ.*127` + // amd64:`SETHI` + return x >= 128 +} + +func ge128Signed32(x int32) bool { + // amd64:`CMPL.*127` + // amd64:`SETGT` + return x >= 128 +} + +func ge128Signed64(x int64) bool { + // amd64:`CMPQ.*127` + // amd64:`SETGT` + return x >= 128 +} + func cmpToCmnGreaterThanEqual(a, b, c, d int) int { var c1, c2, c3, c4 int // arm64:`CMN`,`CSET\tPL`,-`CMP` diff --git a/test/codegen/floats.go b/test/codegen/floats.go index 1b85eba35249c9..2a5cf3995781e6 100644 --- a/test/codegen/floats.go +++ b/test/codegen/floats.go @@ -74,6 +74,7 @@ func FusedAdd32(x, y, z float32) float32 { // arm64:"FMADDS" // loong64:"FMADDF\t" // riscv64:"FMADDS\t" + // amd64/v3:"VFMADD231SS\t" return x*y + z } @@ -98,6 +99,7 @@ func FusedAdd64(x, y, z float64) float64 { // arm64:"FMADDD" // loong64:"FMADDD\t" // riscv64:"FMADDD\t" + // amd64/v3:"VFMADD231SD\t" return x*y + z } diff --git a/test/codegen/ifaces.go b/test/codegen/ifaces.go index 2be3fa5146a7ac..cc67a047405445 100644 --- a/test/codegen/ifaces.go +++ b/test/codegen/ifaces.go @@ -25,3 +25,39 @@ func ConvToM(x any) I { // arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)` return x.(I) } + +func e1(x any, y *int) bool { + // amd64:-`.*faceeq`,`SETEQ` + // arm64:-`.*faceeq`,`CSET\tEQ` + return x == y +} + +func e2(x any, y *int) bool { + // amd64:-`.*faceeq`,`SETEQ` + // arm64:-`.*faceeq`,`CSET\tEQ` + return y == x +} + +type E *int + +func e3(x any, y E) bool { + // amd64:-`.*faceeq`,`SETEQ` + // arm64:-`.*faceeq`,`CSET\tEQ` + return x == y +} + +type T int + +func (t *T) M() {} + +func i1(x I, y *T) bool { + // amd64:-`.*faceeq`,`SETEQ` + // arm64:-`.*faceeq`,`CSET\tEQ` + return x == y +} + +func i2(x I, y *T) bool { + // amd64:-`.*faceeq`,`SETEQ` + // arm64:-`.*faceeq`,`CSET\tEQ` + return y == x +} diff --git a/test/codegen/issue70409.go b/test/codegen/issue70409.go new file mode 100644 index 00000000000000..bfb4560582e4f0 --- /dev/null +++ b/test/codegen/issue70409.go @@ -0,0 +1,20 @@ +// asmcheck -gcflags=-d=ssa/check/on + +// Copyright 2024 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. + +package codegen + +// amd64:-"MOVQ" +func foo(v uint64) (b [8]byte) { + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) + return b +} diff --git a/test/codegen/maps.go b/test/codegen/maps.go index d7cf6534ad907a..860b2c2cbd23cd 100644 --- a/test/codegen/maps.go +++ b/test/codegen/maps.go @@ -4,11 +4,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// TODO(#54766): Temporarily disable for swissmap, which have fast variants -// disabled. This test expects fast variants. -// -//go:build !goexperiment.swissmap - package codegen // This file contains code generation tests related to the handling of @@ -71,6 +66,28 @@ func LookupStringConversionKeyedArrayLit(m map[[2]string]int, bytes []byte) int return m[[2]string{0: string(bytes)}] } +func LookupStringConversion1(m map[string]int, bytes []byte) int { + // amd64:-`.*runtime\.slicebytetostring\(` + s := string(bytes) + return m[s] +} +func LookupStringConversion2(m *map[string]int, bytes []byte) int { + // amd64:-`.*runtime\.slicebytetostring\(` + s := string(bytes) + return (*m)[s] +} +func LookupStringConversion3(m map[string]int, bytes []byte) (int, bool) { + // amd64:-`.*runtime\.slicebytetostring\(` + s := string(bytes) + r, ok := m[s] + return r, ok +} +func DeleteStringConversion(m map[string]int, bytes []byte) { + // amd64:-`.*runtime\.slicebytetostring\(` + s := string(bytes) + delete(m, s) +} + // ------------------- // // Map Clear // // ------------------- // @@ -79,7 +96,7 @@ func LookupStringConversionKeyedArrayLit(m map[[2]string]int, bytes []byte) int func MapClearReflexive(m map[int]int) { // amd64:`.*runtime\.mapclear` - // amd64:-`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.(mapiterinit|mapIterStart)` for k := range m { delete(m, k) } @@ -88,7 +105,7 @@ func MapClearReflexive(m map[int]int) { func MapClearIndirect(m map[int]int) { s := struct{ m map[int]int }{m: m} // amd64:`.*runtime\.mapclear` - // amd64:-`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.(mapiterinit|mapIterStart)` for k := range s.m { delete(s.m, k) } @@ -96,14 +113,14 @@ func MapClearIndirect(m map[int]int) { func MapClearPointer(m map[*byte]int) { // amd64:`.*runtime\.mapclear` - // amd64:-`.*runtime\.mapiterinit` + // amd64:-`.*runtime\.(mapiterinit|mapIterStart)` for k := range m { delete(m, k) } } func MapClearNotReflexive(m map[float64]int) { - // amd64:`.*runtime\.mapiterinit` + // amd64:`.*runtime\.(mapiterinit|mapIterStart)` // amd64:-`.*runtime\.mapclear` for k := range m { delete(m, k) @@ -111,7 +128,7 @@ func MapClearNotReflexive(m map[float64]int) { } func MapClearInterface(m map[interface{}]int) { - // amd64:`.*runtime\.mapiterinit` + // amd64:`.*runtime\.(mapiterinit|mapIterStart)` // amd64:-`.*runtime\.mapclear` for k := range m { delete(m, k) @@ -120,7 +137,7 @@ func MapClearInterface(m map[interface{}]int) { func MapClearSideEffect(m map[int]int) int { k := 0 - // amd64:`.*runtime\.mapiterinit` + // amd64:`.*runtime\.(mapiterinit|mapIterStart)` // amd64:-`.*runtime\.mapclear` for k = range m { delete(m, k) diff --git a/test/codegen/math.go b/test/codegen/math.go index 4ce5fa419d2b79..87d9cd7b2715ba 100644 --- a/test/codegen/math.go +++ b/test/codegen/math.go @@ -240,10 +240,11 @@ func nanGenerate64() float64 { // amd64:"DIVSD" z0 := zero / zero - // amd64:"MULSD" + // amd64/v1,amd64/v2:"MULSD" z1 := zero * inf // amd64:"SQRTSD" z2 := math.Sqrt(negone) + // amd64/v3:"VFMADD231SD" return z0 + z1 + z2 } @@ -254,7 +255,8 @@ func nanGenerate32() float32 { // amd64:"DIVSS" z0 := zero / zero - // amd64:"MULSS" + // amd64/v1,amd64/v2:"MULSS" z1 := zero * inf + // amd64/v3:"VFMADD231SS" return z0 + z1 } diff --git a/test/codegen/memcombine.go b/test/codegen/memcombine.go index e1cae0e46983e6..c5744bf8d7f1a2 100644 --- a/test/codegen/memcombine.go +++ b/test/codegen/memcombine.go @@ -899,9 +899,11 @@ func store32le(p *struct{ a, b uint32 }, x uint64) { p.b = uint32(x >> 32) } func store32be(p *struct{ a, b uint32 }, x uint64) { + // arm64:"STPW" // ppc64:"MOVD",-"MOVW",-"SRD" // s390x:"MOVD",-"MOVW",-"SRD" p.a = uint32(x >> 32) + // arm64:-"STPW" // ppc64:-"MOVW",-"SRD" // s390x:-"MOVW",-"SRD" p.b = uint32(x) @@ -970,3 +972,109 @@ func issue70300Reverse(v uint64) (b [8]byte) { b[0] = byte(v) return b } + +// --------------------------------- // +// Arm64 double-register loads // +// --------------------------------- // + +func dwloadI64(p *struct{ a, b int64 }) int64 { + // arm64:"LDP\t" + return p.a + p.b +} +func dwloadI32(p *struct{ a, b int32 }) int32 { + // arm64:"LDPSW\t" + return p.a + p.b +} +func dwloadU32(p *struct{ a, b uint32 }) uint32 { + // arm64:"LDPW\t" + return p.a + p.b +} +func dwloadF64(p *struct{ a, b float64 }) float64 { + // arm64:"FLDPD\t" + return p.a + p.b +} +func dwloadF32(p *struct{ a, b float32 }) float32 { + // arm64:"FLDPS\t" + return p.a + p.b +} + +func dwloadBig(p *struct{ a, b, c, d, e, f int64 }) int64 { + // arm64:"LDP\t\\(", "LDP\t16", "LDP\t32" + return p.c + p.f + p.a + p.e + p.d + p.b +} + +func dwloadArg(a [2]int64) int64 { + // arm64:"LDP\t" + return a[0] + a[1] +} + +func dwloadResult1(p *string) string { + // arm64:"LDP\t\\(R0\\), \\(R0, R1\\)" + return *p +} + +func dwloadResult2(p *[2]int64) (int64, int64) { + // arm64:"LDP\t\\(R0\\), \\(R1, R0\\)" + return p[1], p[0] +} + +// ---------------------------------- // +// Arm64 double-register stores // +// ---------------------------------- // + +func dwstoreI64(p *struct{ a, b int64 }, x, y int64) { + // arm64:"STP\t" + p.a = x + p.b = y +} +func dwstoreI32(p *struct{ a, b int32 }, x, y int32) { + // arm64:"STPW\t" + p.a = x + p.b = y +} +func dwstoreF64(p *struct{ a, b float64 }, x, y float64) { + // arm64:"FSTPD\t" + p.a = x + p.b = y +} +func dwstoreF32(p *struct{ a, b float32 }, x, y float32) { + // arm64:"FSTPS\t" + p.a = x + p.b = y +} + +func dwstoreBig(p *struct{ a, b, c, d, e, f int64 }, a, b, c, d, e, f int64) { + // This is not perfect. We merge b+a, then d+e, then c and f have no pair. + p.c = c + p.f = f + // arm64:`STP\s\(R[0-9]+, R[0-9]+\), \(R[0-9]+\)` + p.a = a + // arm64:`STP\s\(R[0-9]+, R[0-9]+\), 24\(R[0-9]+\)` + p.e = e + p.d = d + p.b = b +} + +func dwstoreRet() [2]int { + // arm64:"STP\t" + return [2]int{5, 6} +} + +func dwstoreLocal(i int) int64 { + var a [2]int64 + a[0] = 5 + // arm64:"STP\t" + a[1] = 6 + return a[i] +} + +func dwstoreOrder(p *struct { + a, b int64 + c, d, e, f bool +}, a, b int64) { + // arm64:"STP\t" + p.a = a + p.c = true + p.e = true + p.b = b +} diff --git a/test/codegen/shift.go b/test/codegen/shift.go index 2d8cf868571b7d..52efefb0ed828c 100644 --- a/test/codegen/shift.go +++ b/test/codegen/shift.go @@ -58,6 +58,16 @@ func rshConst64x64Overflow8(v int8) int64 { return int64(v) >> 8 } +func lshConst32x1(v int32) int32 { + // amd64:"ADDL", -"SHLL" + return v << 1 +} + +func lshConst64x1(v int64) int64 { + // amd64:"ADDQ", -"SHLQ" + return v << 1 +} + func lshConst32x64(v int32) int32 { // ppc64x:"SLW" // riscv64:"SLLI",-"AND",-"SLTIU", -"MOVW" @@ -94,6 +104,26 @@ func rshConst64x32(v int64) int64 { return v >> uint32(33) } +func lshConst32x1Add(x int32) int32 { + // amd64:"SHLL\t[$]2" + return (x + x) << 1 +} + +func lshConst64x1Add(x int64) int64 { + // amd64:"SHLQ\t[$]2" + return (x + x) << 1 +} + +func lshConst32x2Add(x int32) int32 { + // amd64:"SHLL\t[$]3" + return (x + x) << 2 +} + +func lshConst64x2Add(x int64) int64 { + // amd64:"SHLQ\t[$]3" + return (x + x) << 2 +} + // ------------------ // // masked shifts // // ------------------ // diff --git a/test/codegen/switch.go b/test/codegen/switch.go index 980ea7056192f3..509343110a32db 100644 --- a/test/codegen/switch.go +++ b/test/codegen/switch.go @@ -183,3 +183,17 @@ func interfaceConv(x IJ) I { // arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU\t16\(R0\)`,`MOVD\t\(R.*\)\(R.*\)` return x } + +// Make sure we can constant fold after inlining. See issue 71699. +func stringSwitchInlineable(s string) { + switch s { + case "foo", "bar", "baz", "goo": + default: + println("no") + } +} +func stringSwitch() { + // amd64:-"CMP",-"CALL" + // arm64:-"CMP",-"CALL" + stringSwitchInlineable("foo") +} diff --git a/test/codegen/writebarrier.go b/test/codegen/writebarrier.go index e125973e7cf09f..c3c39c58f7901c 100644 --- a/test/codegen/writebarrier.go +++ b/test/codegen/writebarrier.go @@ -63,3 +63,41 @@ func trickyWriteNil(p *int, q **int) { *q = p } } + +type S struct { + a, b string + c *int +} + +var g1, g2 *int + +func issue71228(dst *S, ptr *int) { + // Make sure that the non-write-barrier write. + // "sp.c = ptr" happens before the large write + // barrier "*dst = *sp". We approximate testing + // that by ensuring that two global variable write + // barriers aren't combined. + _ = *dst + var s S + sp := &s + //amd64:`.*runtime[.]gcWriteBarrier1` + g1 = nil + sp.c = ptr // outside of any write barrier + //amd64:`.*runtime[.]gcWriteBarrier1` + g2 = nil + //amd64:`.*runtime[.]wbMove` + *dst = *sp +} + +func writeDouble(p *[2]*int, x, y *int) { + // arm64: `LDP\s`, `STP\s\(R[0-9]+, R[0-9]+\), \(`, + p[0] = x + // arm64: `STP\s\(R[0-9]+, R[0-9]+\), 16\(`, + p[1] = y +} + +func writeDoubleNil(p *[2]*int) { + // arm64: `LDP\s`, `STP\s\(R[0-9]+, R[0-9]+\),`, `STP\s\(ZR, ZR\),` + p[0] = nil + p[1] = nil +} diff --git a/test/fixedbugs/issue71225.go b/test/fixedbugs/issue71225.go new file mode 100644 index 00000000000000..8a6b313edaa28c --- /dev/null +++ b/test/fixedbugs/issue71225.go @@ -0,0 +1,19 @@ +// build + +//go:build cgo + +// Copyright 2025 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. + +package main + +// #cgo CFLAGS: -Werror -Wunused-parameter +import "C" + +func main() { +} + +//export Fn +func Fn() { +} diff --git a/test/fixedbugs/issue71226.go b/test/fixedbugs/issue71226.go new file mode 100644 index 00000000000000..5df05e3b29c86d --- /dev/null +++ b/test/fixedbugs/issue71226.go @@ -0,0 +1,29 @@ +// build + +//go:build cgo + +// Copyright 2025 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. + +package main + +/* +#cgo CFLAGS: -Werror -Wimplicit-function-declaration + +#include + +static void CFn(_GoString_ gostr) { + printf("%.*s\n", (int)(_GoStringLen(gostr)), _GoStringPtr(gostr)); +} +*/ +import "C" + +func main() { + C.CFn("hello, world") +} + +// The bug only occurs if there is an exported function. +//export Fn +func Fn() { +} diff --git a/test/fixedbugs/issue71759.go b/test/fixedbugs/issue71759.go new file mode 100644 index 00000000000000..8134ff041b1505 --- /dev/null +++ b/test/fixedbugs/issue71759.go @@ -0,0 +1,20 @@ +// run + +// Copyright 2025 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. + +package main + +//go:noinline +func f(p *[2]int32) (int64, int64) { + return int64(p[0]), int64(p[1]) +} + +func main() { + p := [2]int32{-1, -1} + x, y := f(&p) + if x != -1 || y != -1 { + println(x, y) + } +} diff --git a/test/live.go b/test/live.go index 250a77cdac4614..c0b0fcd274a0ca 100644 --- a/test/live.go +++ b/test/live.go @@ -458,14 +458,14 @@ func f28(b bool) { func f29(b bool) { if b { - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } diff --git a/test/live_regabi.go b/test/live_regabi.go index 090e2ec57776fe..35f874ecc365f6 100644 --- a/test/live_regabi.go +++ b/test/live_regabi.go @@ -456,14 +456,14 @@ func f28(b bool) { func f29(b bool) { if b { - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" "stack object .autotmp_[0-9]+ (runtime.hiter|internal/runtime/maps.Iter)$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } - for k := range m { // ERROR "live at call to mapiterinit: .autotmp_[0-9]+$" "live at call to mapiternext: .autotmp_[0-9]+$" + for k := range m { // ERROR "live at call to (mapiterinit|mapIterStart): .autotmp_[0-9]+$" "live at call to (mapiternext|mapIterNext): .autotmp_[0-9]+$" printstring(k) // ERROR "live at call to printstring: .autotmp_[0-9]+$" } } diff --git a/test/prove.go b/test/prove.go index edfd8908a2d8cf..908b05c7fa5328 100644 --- a/test/prove.go +++ b/test/prove.go @@ -1712,6 +1712,24 @@ func clampedIdx2(x []int, i int) int { return x[max(min(i, len(x)-1), 0)] // TODO: can't get rid of this bounds check yet } +func cvtBoolToUint8Disprove(b bool) byte { + var c byte + if b { + c = 1 + } + if c == 2 { // ERROR "Disproved Eq8" + c = 3 + } + return c +} +func cvtBoolToUint8BCE(b bool, a [2]int64) int64 { + c := byte(0) + if b { + c = 1 + } + return a[c] // ERROR "Proved IsInBounds$" +} + //go:noinline func useInt(a int) { } diff --git a/test/tighten.go b/test/tighten.go index 92ed2492b26e74..d85dfecbb0237e 100644 --- a/test/tighten.go +++ b/test/tighten.go @@ -9,14 +9,20 @@ package main var ( - e any - ts uint16 + ga, gb, gc, gd int ) func moveValuesWithMemoryArg(len int) { for n := 0; n < len; n++ { - // Load of e.data is lowed as a MOVDload op, which has a memory - // argument. It's moved near where it's used. - _ = e != ts // ERROR "MOVDload is moved$" "MOVDaddr is moved$" + // Loads of b and d can be delayed until inside the outer "if". + a := ga + b := gb // ERROR "MOVDload is moved$" + c := gc + d := gd // ERROR "MOVDload is moved$" + if a == c { + if b == d { + return + } + } } } diff --git a/test/weak.go b/test/weak.go new file mode 100644 index 00000000000000..ca3ec797fc2366 --- /dev/null +++ b/test/weak.go @@ -0,0 +1,24 @@ +// errorcheck + +// Copyright 2025 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. + +// Test weak pointers. + +package p + +import ( + "runtime" + "weak" +) + +// Adapted from example in https://github.com/golang/go/issues/67552#issuecomment-2639661220 +func conversion() { + p := "hello" + a := weak.Make(&p) + b := (weak.Pointer[*byte])(a) // ERROR "cannot convert a \(variable of struct type weak\.Pointer\[string\]\) to type weak.Pointer\[\*byte\]" + c := b.Value() + println(**c) + runtime.KeepAlive(p) +}