Skip to content

Commit 4179116

Browse files
authored
Merge pull request #264 from golang-fips/gencopt
Autogenerate cgo optimization directives
2 parents b136e57 + fb72807 commit 4179116

File tree

6 files changed

+123
-69
lines changed

6 files changed

+123
-69
lines changed

cmd/mkcgo/generate.go

+24-1
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,19 @@ func generateGo(src *mkcgo.Source, w io.Writer) {
1818

1919
// This block outputs C header includes and forward declarations for loader functions.
2020
fmt.Fprintf(w, "/*\n")
21-
fmt.Fprintf(w, "#cgo CFLAGS: -Wno-attributes\n")
21+
fmt.Fprintf(w, "#cgo CFLAGS: -Wno-attributes\n\n")
2222
if *includeHeader != "" {
2323
fmt.Fprintf(w, "#include \"%s\"\n", *includeHeader)
2424
}
2525
for _, file := range src.Files {
2626
fmt.Fprintf(w, "#include %q\n", file)
2727
}
28+
fmt.Fprintf(w, "\n")
2829
for _, tag := range src.Tags() {
2930
fmt.Fprintf(w, "void __mkcgoLoad_%s(void* handle);\n", tag)
3031
fmt.Fprintf(w, "void __mkcgoUnload_%s();\n", tag)
3132
}
33+
fmt.Fprintf(w, "\n")
3234
for _, fn := range src.Funcs {
3335
if fn.Optional {
3436
fmt.Fprintf(w, "int %s_Available();\n", fn.ImportName)
@@ -75,6 +77,27 @@ func generateGo(src *mkcgo.Source, w io.Writer) {
7577
}
7678
}
7779

80+
// generateGo124 generates Go source code Go 1.24 and later.
81+
func generateGo124(src *mkcgo.Source, w io.Writer) {
82+
// Output header notice and package declaration.
83+
fmt.Fprintf(w, "// Code generated by mkcgo. DO NOT EDIT.\n\n")
84+
fmt.Fprintf(w, "//go:build go1.24 && !cmd_go_bootstrap\n\n")
85+
fmt.Fprintf(w, "package %s\n\n", *packageName)
86+
87+
// This block outputs C header includes and forward declarations for loader functions.
88+
fmt.Fprintf(w, "/*\n")
89+
for _, fn := range src.Funcs {
90+
if fn.NoEscape {
91+
fmt.Fprintf(w, "#cgo noescape %s\n", fn.CName)
92+
}
93+
if fn.NoCallback {
94+
fmt.Fprintf(w, "#cgo nocallback %s\n", fn.CName)
95+
}
96+
}
97+
fmt.Fprintf(w, "*/\n")
98+
fmt.Fprintf(w, "import \"C\"\n")
99+
}
100+
78101
// generateGoEnums generates Go enum values for C enums.
79102
func generateGoEnums(enums []*mkcgo.Enum, w io.Writer) {
80103
if len(enums) == 0 {

cmd/mkcgo/main.go

+39-29
Original file line numberDiff line numberDiff line change
@@ -40,41 +40,38 @@ func main() {
4040
log.Fatal(err)
4141
}
4242

43-
var buf, cbuf bytes.Buffer
44-
generateGo(src, &buf)
43+
var gobuf, go124buf, cbuf bytes.Buffer
44+
generateGo(src, &gobuf)
45+
generateGo124(src, &go124buf)
4546
generateC(src, &cbuf)
4647

4748
// Format the generated Go source code.
48-
data, err := format.Source(buf.Bytes())
49-
if err != nil {
50-
log.Printf("failed to format source: %v", err)
51-
f, err := writeTempSourceFile(buf.Bytes())
52-
if err != nil {
53-
log.Fatalf("failed to write unformatted source to file: %v", err)
54-
}
55-
log.Fatalf("for diagnosis, wrote unformatted source to %v", f)
56-
}
49+
godata := goformat(gobuf.Bytes())
50+
go124data := goformat(go124buf.Bytes())
5751

58-
// Write output. If no explicit output file is specified,
59-
// // write both Go and C output to stdout.
52+
var baseName string
6053
if *fileName == "" {
61-
for _, d := range []struct {
62-
name string
63-
data []byte
64-
}{
65-
{"Go", data},
66-
{"C", cbuf.Bytes()},
67-
} {
68-
os.Stdout.WriteString("// === " + d.name + " ===\n\n")
69-
if _, err = os.Stdout.Write(d.data); err != nil {
70-
log.Fatal(err)
71-
}
72-
}
54+
baseName = "mkcgo"
7355
} else {
74-
err = os.WriteFile(*fileName, data, 0o644)
75-
if err == nil {
76-
cfileName := strings.TrimSuffix(*fileName, ".go") + ".c"
77-
err = os.WriteFile(cfileName, cbuf.Bytes(), 0o644)
56+
baseName = strings.TrimSuffix(*fileName, ".go")
57+
}
58+
59+
for _, d := range []struct {
60+
name string
61+
data []byte
62+
}{
63+
{baseName + ".go", godata},
64+
{baseName + "_go124.go", go124data},
65+
{baseName + ".c", cbuf.Bytes()},
66+
} {
67+
var err error
68+
if *fileName == "" {
69+
// Write output. If no explicit output file is specified,
70+
// // write both Go and C output to stdout.
71+
os.Stdout.WriteString("// === " + d.name + " ===\n\n")
72+
_, err = os.Stdout.Write(d.data)
73+
} else {
74+
err = os.WriteFile(d.name, d.data, 0o644)
7875
}
7976
if err != nil {
8077
log.Fatal(err)
@@ -97,3 +94,16 @@ func writeTempSourceFile(data []byte) (string, error) {
9794
}
9895
return f.Name(), nil
9996
}
97+
98+
func goformat(data []byte) []byte {
99+
data, err := format.Source(data)
100+
if err != nil {
101+
log.Printf("failed to format source: %v", err)
102+
f, err := writeTempSourceFile(data)
103+
if err != nil {
104+
log.Fatalf("failed to write unformatted source to file: %v", err)
105+
}
106+
log.Fatalf("for diagnosis, wrote unformatted source to %v", f)
107+
}
108+
return data
109+
}

internal/mkcgo/parse.go

+22
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ type FuncAttributes struct {
1717
Optional bool
1818
NoError bool
1919
ErrCond string
20+
NoEscape bool
21+
NoCallback bool
2022
}
2123

2224
type attribute struct {
@@ -77,6 +79,22 @@ var attributes = [...]attribute{
7779
return nil
7880
},
7981
},
82+
{
83+
name: "noescape",
84+
description: "The C function does not keep a copy of the Go pointer.",
85+
handle: func(opts *FuncAttributes, s ...string) error {
86+
opts.NoEscape = true
87+
return nil
88+
},
89+
},
90+
{
91+
name: "nocallback",
92+
description: "The C function does not call back into Go.",
93+
handle: func(opts *FuncAttributes, s ...string) error {
94+
opts.NoCallback = true
95+
return nil
96+
},
97+
},
8098
}
8199

82100
// Parse parses files listed in fs and extracts all syscall
@@ -371,6 +389,10 @@ func extractFunctionAttributes(s string, fnAttrs *FuncAttributes) (string, error
371389
if !found {
372390
return "", errors.New("unbalanced parentheses in line: " + s)
373391
}
392+
body = trim(body)
393+
if len(body) > 0 && body[0] != ',' {
394+
return "", errors.New("parameters must be separated by commas in line: " + s)
395+
}
374396
body = strings.TrimPrefix(body, ",")
375397
} else if idxComma == -1 && idxParen == -1 {
376398
// The attribute has no arguments and is the last one.

shims.h

+17-12
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ typedef int point_conversion_form_t;
117117
// - 3: OpenSSL 3.0 or later
118118
// - 111: OpenSSL 1.1.1 or later
119119

120+
// The noescape/nocallback attributes are performance optimizations.
121+
// Only add functions that have been observed to benefit from these
122+
// directives, not every function that is merely expected to meet
123+
// the noescape/nocallback criteria.
124+
120125
// ERR API
121126
void ERR_error_string_n(unsigned long e, char *buf, size_t len);
122127
void ERR_clear_error(void) __attribute__((tag(""),tag("init_3")));
@@ -150,7 +155,7 @@ _OSSL_PROVIDER_PTR OSSL_PROVIDER_try_load(_OSSL_LIB_CTX_PTR libctx, const char *
150155
const char *OSSL_PROVIDER_get0_name(const _OSSL_PROVIDER_PTR prov) __attribute__((tag("3"),noerror));
151156

152157
// RAND API
153-
int RAND_bytes(unsigned char *arg0, int arg1);
158+
int RAND_bytes(unsigned char *arg0, int arg1) __attribute__((noescape,nocallback));
154159

155160
// EVP_MD API
156161
_EVP_MD_PTR EVP_MD_fetch(_OSSL_LIB_CTX_PTR ctx, const char *algorithm, const char *properties) __attribute__((tag("3"),tag("init_3")));
@@ -180,12 +185,12 @@ _EVP_MD_CTX_PTR EVP_MD_CTX_new(void);
180185
void EVP_MD_CTX_free(_EVP_MD_CTX_PTR ctx);
181186
int EVP_MD_CTX_copy(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in);
182187
int EVP_MD_CTX_copy_ex(_EVP_MD_CTX_PTR out, const _EVP_MD_CTX_PTR in);
183-
int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const _EVP_MD_PTR type, _ENGINE_PTR impl);
188+
int EVP_Digest(const void *data, size_t count, unsigned char *md, unsigned int *size, const _EVP_MD_PTR type, _ENGINE_PTR impl) __attribute__((noescape,nocallback));
184189
int EVP_DigestInit_ex(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type, _ENGINE_PTR impl);
185190
int EVP_DigestInit(_EVP_MD_CTX_PTR ctx, const _EVP_MD_PTR type);
186-
int EVP_DigestUpdate(_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt);
191+
int EVP_DigestUpdate(_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt) __attribute__((noescape,nocallback));
187192
int EVP_DigestFinal_ex(_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s);
188-
int EVP_DigestSign(_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen) __attribute__((tag("111")));
193+
int EVP_DigestSign(_EVP_MD_CTX_PTR ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen) __attribute__((tag("111"),noescape,nocallback));
189194
int EVP_DigestSignInit(_EVP_MD_CTX_PTR ctx, _EVP_PKEY_CTX_PTR *pctx, const _EVP_MD_PTR type, _ENGINE_PTR e, _EVP_PKEY_PTR pkey);
190195
int EVP_DigestSignFinal(_EVP_MD_CTX_PTR ctx, unsigned char *sig, size_t *siglen);
191196
int EVP_DigestVerifyInit(_EVP_MD_CTX_PTR ctx, _EVP_PKEY_CTX_PTR *pctx, const _EVP_MD_PTR type, _ENGINE_PTR e, _EVP_PKEY_PTR pkey);
@@ -229,13 +234,13 @@ int EVP_CIPHER_CTX_set_key_length(_EVP_CIPHER_CTX_PTR x, int keylen);
229234
void EVP_CIPHER_CTX_free(_EVP_CIPHER_CTX_PTR arg0);
230235
int EVP_CIPHER_CTX_ctrl(_EVP_CIPHER_CTX_PTR ctx, int type, int arg, void *ptr);
231236
int EVP_CipherInit_ex(_EVP_CIPHER_CTX_PTR ctx, const _EVP_CIPHER_PTR type, _ENGINE_PTR impl, const unsigned char *key, const unsigned char *iv, int enc);
232-
int EVP_CipherUpdate(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
237+
int EVP_CipherUpdate(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl) __attribute__((noescape,nocallback));
233238
int EVP_EncryptInit_ex(_EVP_CIPHER_CTX_PTR ctx, const _EVP_CIPHER_PTR type, _ENGINE_PTR impl, const unsigned char *key, const unsigned char *iv);
234-
int EVP_EncryptUpdate(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
235-
int EVP_EncryptFinal_ex(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl);
239+
int EVP_EncryptUpdate(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl) __attribute__((noescape,nocallback));
240+
int EVP_EncryptFinal_ex(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl) __attribute__((noescape,nocallback));
236241
int EVP_DecryptInit_ex(_EVP_CIPHER_CTX_PTR ctx, const _EVP_CIPHER_PTR type, _ENGINE_PTR impl, const unsigned char *key, const unsigned char *iv);
237-
int EVP_DecryptUpdate(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl);
238-
int EVP_DecryptFinal_ex(_EVP_CIPHER_CTX_PTR ctx, unsigned char *outm, int *outl);
242+
int EVP_DecryptUpdate(_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl) __attribute__((noescape,nocallback));
243+
int EVP_DecryptFinal_ex(_EVP_CIPHER_CTX_PTR ctx, unsigned char *outm, int *outl) __attribute__((noescape,nocallback));
239244

240245
// EVP_PKEY API
241246
_EVP_PKEY_PTR EVP_PKEY_new(void);
@@ -254,8 +259,8 @@ int EVP_PKEY_get_bn_param(const _EVP_PKEY_PTR pkey, const char *key_name, _BIGNU
254259
int EVP_PKEY_up_ref(_EVP_PKEY_PTR key) __attribute__((tag("3")));
255260
int EVP_PKEY_set1_EC_KEY(_EVP_PKEY_PTR pkey, _EC_KEY_PTR key) __attribute__((tag("legacy_1")));
256261
int EVP_PKEY_CTX_set0_rsa_oaep_label(_EVP_PKEY_CTX_PTR ctx, void *label, int len) __attribute__((tag("3")));
257-
int EVP_PKEY_get_raw_public_key(const _EVP_PKEY_PTR pkey, unsigned char *pub, size_t *len) __attribute__((tag("111")));
258-
int EVP_PKEY_get_raw_private_key(const _EVP_PKEY_PTR pkey, unsigned char *priv, size_t *len) __attribute__((tag("111")));
262+
int EVP_PKEY_get_raw_public_key(const _EVP_PKEY_PTR pkey, unsigned char *pub, size_t *len) __attribute__((tag("111"),noescape,nocallback));
263+
int EVP_PKEY_get_raw_private_key(const _EVP_PKEY_PTR pkey, unsigned char *priv, size_t *len) __attribute__((tag("111"),noescape,nocallback));
259264
int EVP_PKEY_fromdata_init(_EVP_PKEY_CTX_PTR ctx) __attribute__((tag("3")));
260265
int EVP_PKEY_fromdata(_EVP_PKEY_CTX_PTR ctx, _EVP_PKEY_PTR *pkey, int selection, _OSSL_PARAM_PTR params) __attribute__((tag("3")));
261266
int EVP_PKEY_paramgen_init(_EVP_PKEY_CTX_PTR ctx);
@@ -272,7 +277,7 @@ int EVP_PKEY_sign(_EVP_PKEY_CTX_PTR arg0, unsigned char *arg1, size_t *arg2, con
272277
int EVP_PKEY_verify(_EVP_PKEY_CTX_PTR ctx, const unsigned char *sig, size_t siglen, const unsigned char *tbs, size_t tbslen);
273278
int EVP_PKEY_derive_init(_EVP_PKEY_CTX_PTR ctx);
274279
int EVP_PKEY_derive_set_peer(_EVP_PKEY_CTX_PTR ctx, _EVP_PKEY_PTR peer);
275-
int EVP_PKEY_derive(_EVP_PKEY_CTX_PTR ctx, unsigned char *key, size_t *keylen);
280+
int EVP_PKEY_derive(_EVP_PKEY_CTX_PTR ctx, unsigned char *key, size_t *keylen) __attribute__((noescape,nocallback));
276281
int EVP_PKEY_public_check_quick(_EVP_PKEY_CTX_PTR ctx) __attribute__((tag("3")));
277282
int EVP_PKEY_private_check(_EVP_PKEY_CTX_PTR ctx) __attribute__((tag("3")));
278283
_EVP_PKEY_PTR EVP_PKEY_Q_keygen(_OSSL_LIB_CTX_PTR ctx, const char *propq, const char *type, ...) __attribute__((tag("3")));

zossl.go

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cgo_go124.go renamed to zossl_go124.go

+18-27
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)