1// Copyright 2015 The BoringSSL Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package main 16 17import ( 18 "bufio" 19 "bytes" 20 "errors" 21 "fmt" 22 "io" 23 "os" 24 "path" 25 "sort" 26 "strconv" 27) 28 29// libraryNames must be kept in sync with the enum in err.h. The generated code 30// will contain static assertions to enforce this. 31var libraryNames = []string{ 32 "NONE", 33 "SYS", 34 "BN", 35 "RSA", 36 "DH", 37 "EVP", 38 "BUF", 39 "OBJ", 40 "PEM", 41 "DSA", 42 "X509", 43 "ASN1", 44 "CONF", 45 "CRYPTO", 46 "EC", 47 "SSL", 48 "BIO", 49 "PKCS7", 50 "PKCS8", 51 "X509V3", 52 "RAND", 53 "ENGINE", 54 "OCSP", 55 "UI", 56 "COMP", 57 "ECDSA", 58 "ECDH", 59 "HMAC", 60 "DIGEST", 61 "CIPHER", 62 "HKDF", 63 "TRUST_TOKEN", 64 "CMS", 65 "USER", 66} 67 68// stringList is a map from uint32 -> string which can output data for a sorted 69// list as C literals. 70type stringList struct { 71 // entries is an array of keys and offsets into |stringData|. The 72 // offsets are in the bottom 15 bits of each uint32 and the key is the 73 // top 17 bits. 74 entries []uint32 75 // internedStrings contains the same strings as are in |stringData|, 76 // but allows for easy deduplication. It maps a string to its offset in 77 // |stringData|. 78 internedStrings map[string]uint32 79 stringData []byte 80} 81 82func newStringList() *stringList { 83 return &stringList{ 84 internedStrings: make(map[string]uint32), 85 } 86} 87 88// offsetMask is the bottom 15 bits. It's a mask that selects the offset from a 89// uint32 in entries. 90const offsetMask = 0x7fff 91 92func (st *stringList) Add(key uint32, value string) error { 93 if key&offsetMask != 0 { 94 return errors.New("need bottom 15 bits of the key for the offset") 95 } 96 offset, ok := st.internedStrings[value] 97 if !ok { 98 offset = uint32(len(st.stringData)) 99 if offset&offsetMask != offset { 100 return errors.New("stringList overflow") 101 } 102 st.stringData = append(st.stringData, []byte(value)...) 103 st.stringData = append(st.stringData, 0) 104 st.internedStrings[value] = offset 105 } 106 107 for _, existing := range st.entries { 108 if existing>>15 == key>>15 { 109 panic("duplicate entry") 110 } 111 } 112 st.entries = append(st.entries, key|offset) 113 return nil 114} 115 116func (st *stringList) buildList() []uint32 { 117 sort.Slice(st.entries, func(i, j int) bool { return (st.entries[i] >> 15) < (st.entries[j] >> 15) }) 118 return st.entries 119} 120 121type stringWriter interface { 122 io.Writer 123 WriteString(string) (int, error) 124} 125 126func (st *stringList) WriteTo(out stringWriter, name string) { 127 list := st.buildList() 128 values := "kOpenSSL" + name + "Values" 129 out.WriteString("extern const uint32_t " + values + "[];\n") 130 out.WriteString("const uint32_t " + values + "[] = {\n") 131 for _, v := range list { 132 fmt.Fprintf(out, " 0x%x,\n", v) 133 } 134 out.WriteString("};\n\n") 135 out.WriteString("extern const size_t " + values + "Len;\n") 136 out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n") 137 138 stringData := "kOpenSSL" + name + "StringData" 139 out.WriteString("extern const char " + stringData + "[];\n") 140 out.WriteString("const char " + stringData + "[] =\n \"") 141 for i, c := range st.stringData { 142 if c == 0 { 143 out.WriteString("\\0\"\n \"") 144 continue 145 } 146 out.Write(st.stringData[i : i+1]) 147 } 148 out.WriteString("\";\n\n") 149} 150 151type errorData struct { 152 reasons *stringList 153 libraryMap map[string]uint32 154} 155 156func (e *errorData) readErrorDataFile(filename string) error { 157 inFile, err := os.Open(filename) 158 if err != nil { 159 return err 160 } 161 defer inFile.Close() 162 163 scanner := bufio.NewScanner(inFile) 164 comma := []byte(",") 165 166 lineNo := 0 167 for scanner.Scan() { 168 lineNo++ 169 170 line := scanner.Bytes() 171 if len(line) == 0 { 172 continue 173 } 174 parts := bytes.Split(line, comma) 175 if len(parts) != 3 { 176 return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts)) 177 } 178 libNum, ok := e.libraryMap[string(parts[0])] 179 if !ok { 180 return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename) 181 } 182 if libNum >= 64 { 183 return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename) 184 } 185 key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */) 186 if err != nil { 187 return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err) 188 } 189 if key >= 2048 { 190 return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename) 191 } 192 value := string(parts[2]) 193 194 listKey := libNum<<26 | uint32(key)<<15 195 196 err = e.reasons.Add(listKey, value) 197 if err != nil { 198 return err 199 } 200 } 201 202 return scanner.Err() 203} 204 205type ErrDataTask struct { 206 TargetName string 207 Inputs []string 208} 209 210func (t *ErrDataTask) Destination() string { 211 return path.Join("gen", t.TargetName, "err_data.cc") 212} 213 214func (t *ErrDataTask) Run() ([]byte, error) { 215 e := &errorData{ 216 reasons: newStringList(), 217 libraryMap: make(map[string]uint32), 218 } 219 for i, name := range libraryNames { 220 e.libraryMap[name] = uint32(i) + 1 221 } 222 223 for _, input := range t.Inputs { 224 if err := e.readErrorDataFile(input); err != nil { 225 return nil, err 226 } 227 } 228 229 var out bytes.Buffer 230 out.WriteString(`// Copyright 2015 The BoringSSL Authors 231// 232// Licensed under the Apache License, Version 2.0 (the "License"); 233// you may not use this file except in compliance with the License. 234// You may obtain a copy of the License at 235// 236// https://www.apache.org/licenses/LICENSE-2.0 237// 238// Unless required by applicable law or agreed to in writing, software 239// distributed under the License is distributed on an "AS IS" BASIS, 240// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 241// See the License for the specific language governing permissions and 242// limitations under the License. 243 244// This file was generated by go run ./util/pregenerate. 245 246#include <openssl/base.h> 247#include <openssl/err.h> 248 249#include <assert.h> 250 251`) 252 253 for i, name := range libraryNames { 254 fmt.Fprintf(&out, "static_assert(ERR_LIB_%s == %d, \"library value changed\");\n", name, i+1) 255 } 256 fmt.Fprintf(&out, "static_assert(ERR_NUM_LIBS == %d, \"number of libraries changed\");\n", len(libraryNames)+1) 257 out.WriteString("\n") 258 259 e.reasons.WriteTo(&out, "Reason") 260 return out.Bytes(), nil 261} 262