1// Copyright 2015 The BoringSSL Authors 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 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 "USER", 65} 66 67// stringList is a map from uint32 -> string which can output data for a sorted 68// list as C literals. 69type stringList struct { 70 // entries is an array of keys and offsets into |stringData|. The 71 // offsets are in the bottom 15 bits of each uint32 and the key is the 72 // top 17 bits. 73 entries []uint32 74 // internedStrings contains the same strings as are in |stringData|, 75 // but allows for easy deduplication. It maps a string to its offset in 76 // |stringData|. 77 internedStrings map[string]uint32 78 stringData []byte 79} 80 81func newStringList() *stringList { 82 return &stringList{ 83 internedStrings: make(map[string]uint32), 84 } 85} 86 87// offsetMask is the bottom 15 bits. It's a mask that selects the offset from a 88// uint32 in entries. 89const offsetMask = 0x7fff 90 91func (st *stringList) Add(key uint32, value string) error { 92 if key&offsetMask != 0 { 93 return errors.New("need bottom 15 bits of the key for the offset") 94 } 95 offset, ok := st.internedStrings[value] 96 if !ok { 97 offset = uint32(len(st.stringData)) 98 if offset&offsetMask != offset { 99 return errors.New("stringList overflow") 100 } 101 st.stringData = append(st.stringData, []byte(value)...) 102 st.stringData = append(st.stringData, 0) 103 st.internedStrings[value] = offset 104 } 105 106 for _, existing := range st.entries { 107 if existing>>15 == key>>15 { 108 panic("duplicate entry") 109 } 110 } 111 st.entries = append(st.entries, key|offset) 112 return nil 113} 114 115func (st *stringList) buildList() []uint32 { 116 sort.Slice(st.entries, func(i, j int) bool { return (st.entries[i] >> 15) < (st.entries[j] >> 15) }) 117 return st.entries 118} 119 120type stringWriter interface { 121 io.Writer 122 WriteString(string) (int, error) 123} 124 125func (st *stringList) WriteTo(out stringWriter, name string) { 126 list := st.buildList() 127 values := "kOpenSSL" + name + "Values" 128 out.WriteString("extern const uint32_t " + values + "[];\n") 129 out.WriteString("const uint32_t " + values + "[] = {\n") 130 for _, v := range list { 131 fmt.Fprintf(out, " 0x%x,\n", v) 132 } 133 out.WriteString("};\n\n") 134 out.WriteString("extern const size_t " + values + "Len;\n") 135 out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n") 136 137 stringData := "kOpenSSL" + name + "StringData" 138 out.WriteString("extern const char " + stringData + "[];\n") 139 out.WriteString("const char " + stringData + "[] =\n \"") 140 for i, c := range st.stringData { 141 if c == 0 { 142 out.WriteString("\\0\"\n \"") 143 continue 144 } 145 out.Write(st.stringData[i : i+1]) 146 } 147 out.WriteString("\";\n\n") 148} 149 150type errorData struct { 151 reasons *stringList 152 libraryMap map[string]uint32 153} 154 155func (e *errorData) readErrorDataFile(filename string) error { 156 inFile, err := os.Open(filename) 157 if err != nil { 158 return err 159 } 160 defer inFile.Close() 161 162 scanner := bufio.NewScanner(inFile) 163 comma := []byte(",") 164 165 lineNo := 0 166 for scanner.Scan() { 167 lineNo++ 168 169 line := scanner.Bytes() 170 if len(line) == 0 { 171 continue 172 } 173 parts := bytes.Split(line, comma) 174 if len(parts) != 3 { 175 return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts)) 176 } 177 libNum, ok := e.libraryMap[string(parts[0])] 178 if !ok { 179 return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename) 180 } 181 if libNum >= 64 { 182 return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename) 183 } 184 key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */) 185 if err != nil { 186 return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err) 187 } 188 if key >= 2048 { 189 return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename) 190 } 191 value := string(parts[2]) 192 193 listKey := libNum<<26 | uint32(key)<<15 194 195 err = e.reasons.Add(listKey, value) 196 if err != nil { 197 return err 198 } 199 } 200 201 return scanner.Err() 202} 203 204type ErrDataTask struct { 205 TargetName string 206 Inputs []string 207} 208 209func (t *ErrDataTask) Destination() string { 210 return path.Join("gen", t.TargetName, "err_data.cc") 211} 212 213func (t *ErrDataTask) Run() ([]byte, error) { 214 e := &errorData{ 215 reasons: newStringList(), 216 libraryMap: make(map[string]uint32), 217 } 218 for i, name := range libraryNames { 219 e.libraryMap[name] = uint32(i) + 1 220 } 221 222 for _, input := range t.Inputs { 223 if err := e.readErrorDataFile(input); err != nil { 224 return nil, err 225 } 226 } 227 228 var out bytes.Buffer 229 out.WriteString(`/* Copyright 2015 The BoringSSL Authors 230 * 231 * Permission to use, copy, modify, and/or distribute this software for any 232 * purpose with or without fee is hereby granted, provided that the above 233 * copyright notice and this permission notice appear in all copies. 234 * 235 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 236 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 237 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 238 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 239 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 240 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 241 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 242 243 /* This file was generated by go run ./util/pregenerate. */ 244 245#include <openssl/base.h> 246#include <openssl/err.h> 247 248#include <assert.h> 249 250`) 251 252 for i, name := range libraryNames { 253 fmt.Fprintf(&out, "static_assert(ERR_LIB_%s == %d, \"library value changed\");\n", name, i+1) 254 } 255 fmt.Fprintf(&out, "static_assert(ERR_NUM_LIBS == %d, \"number of libraries changed\");\n", len(libraryNames)+1) 256 out.WriteString("\n") 257 258 e.reasons.WriteTo(&out, "Reason") 259 return out.Bytes(), nil 260} 261