diff options
Diffstat (limited to 'src/tools/mac/upload_system_symbols/arch_reader.go')
-rw-r--r-- | src/tools/mac/upload_system_symbols/arch_reader.go | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/tools/mac/upload_system_symbols/arch_reader.go b/src/tools/mac/upload_system_symbols/arch_reader.go new file mode 100644 index 00000000..0468836e --- /dev/null +++ b/src/tools/mac/upload_system_symbols/arch_reader.go @@ -0,0 +1,268 @@ +/* Copyright 2014, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package main + +import ( + "encoding/binary" + "errors" + "fmt" + "os" + "reflect" + "unsafe" +) + +/* +#include <mach-o/fat.h> +#include <mach-o/loader.h> +#include <string.h> + +#include "arch_constants.h" +*/ +import "C" + +var ( + ErrNotMachO = errors.New("GetMachOImageInfo: file is not a supported Mach-O image") + ErrUnsupportedArch = errors.New("GetMachOImageInfo: unknown architecture detected") +) + +const ( + ArchI386 = "i386" + ArchX86_64 = "x86_64" +) + +type MachOType int + +const ( + MachODylib MachOType = C.kMachHeaderFtypeDylib + MachOBundle = C.kMachHeaderFtypeBundle + MachOExe = C.kMachHeaderFtypeExe +) + +type ImageInfo struct { + Type MachOType + Arch string +} + +// GetMachOImageInfo will read the file at filepath and determine if it is +// Mach-O. If it is, it will return a slice of ImageInfo that describe the +// images in the file (may be more than one if it is a fat image). +func GetMachOImageInfo(filepath string) ([]ImageInfo, error) { + f, err := os.Open(filepath) + if err != nil { + return nil, err + } + defer f.Close() + + // Read the magic number to determine the type of file this is. + var magic uint32 + err = binary.Read(f, binary.LittleEndian, &magic) + if err != nil { + return nil, err + } + + // Rewind the file since the magic number is a field in the header + // structs. + f.Seek(0, os.SEEK_SET) + + switch magic { + case C.kMachHeaderMagic32: + return readThinHeader(f, C.kMachHeaderMagic32) + case C.kMachHeaderMagic64: + return readThinHeader(f, C.kMachHeaderMagic64) + case C.kMachHeaderCigamFat: // Fat header is big-endian but was read in little. + return readFatHeader(f) + } + + return nil, ErrNotMachO +} + +func readThinHeader(f *os.File, expectedMagic uint32) ([]ImageInfo, error) { + var ( + magic, filetype uint32 + cpu C.cpu_type_t + err error + ) + + if expectedMagic == C.kMachHeaderMagic32 { + magic, cpu, filetype, err = readThin32Header(f) + } else if expectedMagic == C.kMachHeaderMagic64 { + magic, cpu, filetype, err = readThin64Header(f) + } else { + panic(fmt.Sprintf("Unexpected magic %#x", magic)) + } + if err != nil { + return nil, err + } + + if magic != expectedMagic { + return nil, fmt.Errorf("readThinHeader: unexpected magic number %#x", magic) + } + + arch := cpuTypeToArch(cpu) + if arch == "" { + return nil, ErrUnsupportedArch + } + return []ImageInfo{{MachOType(filetype), arch}}, nil +} + +func readThin32Header(f *os.File) (uint32, C.cpu_type_t, uint32, error) { + var machHeader C.struct_mach_header + err := readStruct(f, binary.LittleEndian, unsafe.Pointer(&machHeader), C.struct_mach_header{}) + if err != nil { + return 0, 0, 0, err + } + return uint32(machHeader.magic), machHeader.cputype, uint32(machHeader.filetype), nil +} + +func readThin64Header(f *os.File) (uint32, C.cpu_type_t, uint32, error) { + var machHeader C.struct_mach_header_64 + err := readStruct(f, binary.LittleEndian, unsafe.Pointer(&machHeader), C.struct_mach_header_64{}) + if err != nil { + return 0, 0, 0, err + } + return uint32(machHeader.magic), machHeader.cputype, uint32(machHeader.filetype), nil +} + +func readFatHeader(f *os.File) ([]ImageInfo, error) { + var fatHeader C.struct_fat_header + err := readStruct(f, binary.BigEndian, unsafe.Pointer(&fatHeader), C.struct_fat_header{}) + if err != nil { + return nil, err + } + + if fatHeader.magic != C.kMachHeaderMagicFat { + return nil, fmt.Errorf("readFatHeader: unexpected magic number %#x", fatHeader.magic) + } + + // Read the fat_arch headers. + headers := make([]C.struct_fat_arch, fatHeader.nfat_arch) + for i := 0; i < int(fatHeader.nfat_arch); i++ { + var fatArch C.struct_fat_arch + err = readStruct(f, binary.BigEndian, unsafe.Pointer(&fatArch), C.struct_fat_arch{}) + if err != nil { + return nil, fmt.Errorf("readFatHeader: %v", err) + } + headers[i] = fatArch + } + + seenArches := make(map[string]int) + + // Now go to each arch in the fat image and read its mach header. + infos := make([]ImageInfo, 0, len(headers)) + for _, header := range headers { + f.Seek(int64(header.offset), os.SEEK_SET) + + var thinarch []ImageInfo + var expectedArch string + switch header.cputype { + case C.kCPUType_i386: + thinarch, err = readThinHeader(f, C.kMachHeaderMagic32) + expectedArch = ArchI386 + case C.kCPUType_x86_64: + thinarch, err = readThinHeader(f, C.kMachHeaderMagic64) + expectedArch = ArchX86_64 + default: + err = ErrUnsupportedArch + } + + if err != nil { + return nil, err + } + if thinarch[0].Arch != expectedArch { + return nil, fmt.Errorf("readFatHeader: expected arch %d, got %d", thinarch[0].Arch, expectedArch) + } + + infos = append(infos, thinarch[0]) + seenArches[thinarch[0].Arch]++ + } + + for arch, count := range seenArches { + if count != 1 { + return nil, fmt.Errorf("readFatHeader: duplicate arch %s detected", arch) + } + } + + return infos, nil +} + +// TODO(rsesek): Support more arches. +func cpuTypeToArch(cpu C.cpu_type_t) string { + switch cpu { + case C.kCPUType_i386: + return ArchI386 + case C.kCPUType_x86_64: + return ArchX86_64 + default: + return "" + } +} + +// readStruct is a incomplete version of binary.Read that uses unsafe pointers +// to set values in unexported fields. From |f|, this will read the fields of +// the |destType| template instance, in the specified byte |order|, and place +// the resulting memory into |dest|. +func readStruct(f *os.File, order binary.ByteOrder, dest unsafe.Pointer, destType interface{}) error { + rv := reflect.ValueOf(destType) + rt := rv.Type() + destPtr := uintptr(dest) + + for i := 0; i < rv.NumField(); i++ { + field := rv.Field(i) + fieldType := rt.Field(i) + + var vp unsafe.Pointer + var err error + + switch field.Kind() { + case reflect.Int32: + var v int32 + vp = unsafe.Pointer(&v) + err = binary.Read(f, order, &v) + case reflect.Uint32: + var v uint32 + vp = unsafe.Pointer(&v) + err = binary.Read(f, order, &v) + default: + err = fmt.Errorf("readStruct: unsupported type %v", fieldType) + } + + if err != nil { + return err + } + + memcpy(destPtr+fieldType.Offset, vp, fieldType.Type.Size()) + } + return nil +} + +func memcpy(dest uintptr, value unsafe.Pointer, size uintptr) { + C.memcpy(unsafe.Pointer(dest), value, C.size_t(size)) +} |