1// Copyright (c) 2015, Google Inc. 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("const uint32_t " + values + "[] = {\n") 129 for _, v := range list { 130 fmt.Fprintf(out, " 0x%x,\n", v) 131 } 132 out.WriteString("};\n\n") 133 out.WriteString("const size_t " + values + "Len = sizeof(" + values + ") / sizeof(" + values + "[0]);\n\n") 134 135 stringData := "kOpenSSL" + name + "StringData" 136 out.WriteString("const char " + stringData + "[] =\n \"") 137 for i, c := range st.stringData { 138 if c == 0 { 139 out.WriteString("\\0\"\n \"") 140 continue 141 } 142 out.Write(st.stringData[i : i+1]) 143 } 144 out.WriteString("\";\n\n") 145} 146 147type errorData struct { 148 reasons *stringList 149 libraryMap map[string]uint32 150} 151 152func (e *errorData) readErrorDataFile(filename string) error { 153 inFile, err := os.Open(filename) 154 if err != nil { 155 return err 156 } 157 defer inFile.Close() 158 159 scanner := bufio.NewScanner(inFile) 160 comma := []byte(",") 161 162 lineNo := 0 163 for scanner.Scan() { 164 lineNo++ 165 166 line := scanner.Bytes() 167 if len(line) == 0 { 168 continue 169 } 170 parts := bytes.Split(line, comma) 171 if len(parts) != 3 { 172 return fmt.Errorf("bad line %d in %s: found %d values but want 3", lineNo, filename, len(parts)) 173 } 174 libNum, ok := e.libraryMap[string(parts[0])] 175 if !ok { 176 return fmt.Errorf("bad line %d in %s: unknown library", lineNo, filename) 177 } 178 if libNum >= 64 { 179 return fmt.Errorf("bad line %d in %s: library value too large", lineNo, filename) 180 } 181 key, err := strconv.ParseUint(string(parts[1]), 10 /* base */, 32 /* bit size */) 182 if err != nil { 183 return fmt.Errorf("bad line %d in %s: %s", lineNo, filename, err) 184 } 185 if key >= 2048 { 186 return fmt.Errorf("bad line %d in %s: key too large", lineNo, filename) 187 } 188 value := string(parts[2]) 189 190 listKey := libNum<<26 | uint32(key)<<15 191 192 err = e.reasons.Add(listKey, value) 193 if err != nil { 194 return err 195 } 196 } 197 198 return scanner.Err() 199} 200 201type ErrDataTask struct { 202 TargetName string 203 Inputs []string 204} 205 206func (t *ErrDataTask) Destination() string { 207 return path.Join("gen", t.TargetName, "err_data.c") 208} 209 210func (t *ErrDataTask) Run() ([]byte, error) { 211 e := &errorData{ 212 reasons: newStringList(), 213 libraryMap: make(map[string]uint32), 214 } 215 for i, name := range libraryNames { 216 e.libraryMap[name] = uint32(i) + 1 217 } 218 219 for _, input := range t.Inputs { 220 if err := e.readErrorDataFile(input); err != nil { 221 return nil, err 222 } 223 } 224 225 var out bytes.Buffer 226 out.WriteString(`/* Copyright (c) 2015, Google Inc. 227 * 228 * Permission to use, copy, modify, and/or distribute this software for any 229 * purpose with or without fee is hereby granted, provided that the above 230 * copyright notice and this permission notice appear in all copies. 231 * 232 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 233 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 234 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 235 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 236 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 237 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 238 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ 239 240 /* This file was generated by go run ./util/pregenerate. */ 241 242#include <openssl/base.h> 243#include <openssl/err.h> 244 245#include <assert.h> 246 247`) 248 249 for i, name := range libraryNames { 250 fmt.Fprintf(&out, "static_assert(ERR_LIB_%s == %d, \"library value changed\");\n", name, i+1) 251 } 252 fmt.Fprintf(&out, "static_assert(ERR_NUM_LIBS == %d, \"number of libraries changed\");\n", len(libraryNames)+1) 253 out.WriteString("\n") 254 255 e.reasons.WriteTo(&out, "Reason") 256 return out.Bytes(), nil 257} 258