| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package macho implements access to Mach-O object files. | |
| package macho | |
| // High level access to low level data structures. | |
| import ( | |
| "bytes" | |
| "compress/zlib" | |
| "debug/dwarf" | |
| "encoding/binary" | |
| "fmt" | |
| "io" | |
| "os" | |
| "strings" | |
| "unsafe" | |
| ) | |
| // A File represents an open Mach-O file. | |
| type File struct { | |
| FileTOC | |
| Symtab *Symtab | |
| Dysymtab *Dysymtab | |
| closer io.Closer | |
| } | |
| type FileTOC struct { | |
| FileHeader | |
| ByteOrder binary.ByteOrder | |
| Loads []Load | |
| Sections []*Section | |
| } | |
| func (t *FileTOC) AddLoad(l Load) { | |
| t.Loads = append(t.Loads, l) | |
| t.NCommands++ | |
| t.SizeCommands += l.LoadSize(t) | |
| } | |
| // AddSegment adds segment s to the file table of contents, | |
| // and also zeroes out the segment information with the expectation | |
| // that this will be added next. | |
| func (t *FileTOC) AddSegment(s *Segment) { | |
| t.AddLoad(s) | |
| s.Nsect = 0 | |
| s.Firstsect = 0 | |
| } | |
| // Adds section to the most recently added Segment | |
| func (t *FileTOC) AddSection(s *Section) { | |
| g := t.Loads[len(t.Loads)-1].(*Segment) | |
| if g.Nsect == 0 { | |
| g.Firstsect = uint32(len(t.Sections)) | |
| } | |
| g.Nsect++ | |
| t.Sections = append(t.Sections, s) | |
| sectionsize := uint32(unsafe.Sizeof(Section32{})) | |
| if g.Command() == LcSegment64 { | |
| sectionsize = uint32(unsafe.Sizeof(Section64{})) | |
| } | |
| t.SizeCommands += sectionsize | |
| g.Len += sectionsize | |
| } | |
| // A Load represents any Mach-O load command. | |
| type Load interface { | |
| String() string | |
| Command() LoadCmd | |
| LoadSize(*FileTOC) uint32 // Need the TOC for alignment, sigh. | |
| Put([]byte, binary.ByteOrder) int | |
| // command LC_DYLD_INFO_ONLY contains offsets into __LINKEDIT | |
| // e.g., from "otool -l a.out" | |
| // | |
| // Load command 3 | |
| // cmd LC_SEGMENT_64 | |
| // cmdsize 72 | |
| // segname __LINKEDIT | |
| // vmaddr 0x0000000100002000 | |
| // vmsize 0x0000000000001000 | |
| // fileoff 8192 | |
| // filesize 520 | |
| // maxprot 0x00000007 | |
| // initprot 0x00000001 | |
| // nsects 0 | |
| // flags 0x0 | |
| // Load command 4 | |
| // cmd LC_DYLD_INFO_ONLY | |
| // cmdsize 48 | |
| // rebase_off 8192 | |
| // rebase_size 8 | |
| // bind_off 8200 | |
| // bind_size 24 | |
| // weak_bind_off 0 | |
| // weak_bind_size 0 | |
| // lazy_bind_off 8224 | |
| // lazy_bind_size 16 | |
| // export_off 8240 | |
| // export_size 48 | |
| } | |
| // LoadBytes is the uninterpreted bytes of a Mach-O load command. | |
| type LoadBytes []byte | |
| // A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command. | |
| type SegmentHeader struct { | |
| LoadCmd | |
| Len uint32 | |
| Name string // 16 characters or fewer | |
| Addr uint64 // memory address | |
| Memsz uint64 // memory size | |
| Offset uint64 // file offset | |
| Filesz uint64 // number of bytes starting at that file offset | |
| Maxprot uint32 | |
| Prot uint32 | |
| Nsect uint32 | |
| Flag SegFlags | |
| Firstsect uint32 | |
| } | |
| // A Segment represents a Mach-O 32-bit or 64-bit load segment command. | |
| type Segment struct { | |
| SegmentHeader | |
| // Embed ReaderAt for ReadAt method. | |
| // Do not embed SectionReader directly | |
| // to avoid having Read and Seek. | |
| // If a client wants Read and Seek it must use | |
| // Open() to avoid fighting over the seek offset | |
| // with other clients. | |
| io.ReaderAt | |
| sr *io.SectionReader | |
| } | |
| func (s *Segment) Put32(b []byte, o binary.ByteOrder) int { | |
| o.PutUint32(b[0*4:], uint32(s.LoadCmd)) | |
| o.PutUint32(b[1*4:], s.Len) | |
| putAtMost16Bytes(b[2*4:], s.Name) | |
| o.PutUint32(b[6*4:], uint32(s.Addr)) | |
| o.PutUint32(b[7*4:], uint32(s.Memsz)) | |
| o.PutUint32(b[8*4:], uint32(s.Offset)) | |
| o.PutUint32(b[9*4:], uint32(s.Filesz)) | |
| o.PutUint32(b[10*4:], s.Maxprot) | |
| o.PutUint32(b[11*4:], s.Prot) | |
| o.PutUint32(b[12*4:], s.Nsect) | |
| o.PutUint32(b[13*4:], uint32(s.Flag)) | |
| return 14 * 4 | |
| } | |
| func (s *Segment) Put64(b []byte, o binary.ByteOrder) int { | |
| o.PutUint32(b[0*4:], uint32(s.LoadCmd)) | |
| o.PutUint32(b[1*4:], s.Len) | |
| putAtMost16Bytes(b[2*4:], s.Name) | |
| o.PutUint64(b[6*4+0*8:], s.Addr) | |
| o.PutUint64(b[6*4+1*8:], s.Memsz) | |
| o.PutUint64(b[6*4+2*8:], s.Offset) | |
| o.PutUint64(b[6*4+3*8:], s.Filesz) | |
| o.PutUint32(b[6*4+4*8:], s.Maxprot) | |
| o.PutUint32(b[7*4+4*8:], s.Prot) | |
| o.PutUint32(b[8*4+4*8:], s.Nsect) | |
| o.PutUint32(b[9*4+4*8:], uint32(s.Flag)) | |
| return 10*4 + 4*8 | |
| } | |
| // LoadCmdBytes is a command-tagged sequence of bytes. | |
| // This is used for Load Commands that are not (yet) | |
| // interesting to us, and to common up this behavior for | |
| // all those that are. | |
| type LoadCmdBytes struct { | |
| LoadCmd | |
| LoadBytes | |
| } | |
| type SectionHeader struct { | |
| Name string | |
| Seg string | |
| Addr uint64 | |
| Size uint64 | |
| Offset uint32 | |
| Align uint32 | |
| Reloff uint32 | |
| Nreloc uint32 | |
| Flags SecFlags | |
| Reserved1 uint32 | |
| Reserved2 uint32 | |
| Reserved3 uint32 // only present if original was 64-bit | |
| } | |
| // A Reloc represents a Mach-O relocation. | |
| type Reloc struct { | |
| Addr uint32 | |
| Value uint32 | |
| // when Scattered == false && Extern == true, Value is the symbol number. | |
| // when Scattered == false && Extern == false, Value is the section number. | |
| // when Scattered == true, Value is the value that this reloc refers to. | |
| Type uint8 | |
| Len uint8 // 0=byte, 1=word, 2=long, 3=quad | |
| Pcrel bool | |
| Extern bool // valid if Scattered == false | |
| Scattered bool | |
| } | |
| type Section struct { | |
| SectionHeader | |
| Relocs []Reloc | |
| // Embed ReaderAt for ReadAt method. | |
| // Do not embed SectionReader directly | |
| // to avoid having Read and Seek. | |
| // If a client wants Read and Seek it must use | |
| // Open() to avoid fighting over the seek offset | |
| // with other clients. | |
| io.ReaderAt | |
| sr *io.SectionReader | |
| } | |
| func (s *Section) Put32(b []byte, o binary.ByteOrder) int { | |
| putAtMost16Bytes(b[0:], s.Name) | |
| putAtMost16Bytes(b[16:], s.Seg) | |
| o.PutUint32(b[8*4:], uint32(s.Addr)) | |
| o.PutUint32(b[9*4:], uint32(s.Size)) | |
| o.PutUint32(b[10*4:], s.Offset) | |
| o.PutUint32(b[11*4:], s.Align) | |
| o.PutUint32(b[12*4:], s.Reloff) | |
| o.PutUint32(b[13*4:], s.Nreloc) | |
| o.PutUint32(b[14*4:], uint32(s.Flags)) | |
| o.PutUint32(b[15*4:], s.Reserved1) | |
| o.PutUint32(b[16*4:], s.Reserved2) | |
| a := 17 * 4 | |
| return a + s.PutRelocs(b[a:], o) | |
| } | |
| func (s *Section) Put64(b []byte, o binary.ByteOrder) int { | |
| putAtMost16Bytes(b[0:], s.Name) | |
| putAtMost16Bytes(b[16:], s.Seg) | |
| o.PutUint64(b[8*4+0*8:], s.Addr) | |
| o.PutUint64(b[8*4+1*8:], s.Size) | |
| o.PutUint32(b[8*4+2*8:], s.Offset) | |
| o.PutUint32(b[9*4+2*8:], s.Align) | |
| o.PutUint32(b[10*4+2*8:], s.Reloff) | |
| o.PutUint32(b[11*4+2*8:], s.Nreloc) | |
| o.PutUint32(b[12*4+2*8:], uint32(s.Flags)) | |
| o.PutUint32(b[13*4+2*8:], s.Reserved1) | |
| o.PutUint32(b[14*4+2*8:], s.Reserved2) | |
| o.PutUint32(b[15*4+2*8:], s.Reserved3) | |
| a := 16*4 + 2*8 | |
| return a + s.PutRelocs(b[a:], o) | |
| } | |
| func (s *Section) PutRelocs(b []byte, o binary.ByteOrder) int { | |
| a := 0 | |
| for _, r := range s.Relocs { | |
| var ri relocInfo | |
| typ := uint32(r.Type) & (1<<4 - 1) | |
| len := uint32(r.Len) & (1<<2 - 1) | |
| pcrel := uint32(0) | |
| if r.Pcrel { | |
| pcrel = 1 | |
| } | |
| ext := uint32(0) | |
| if r.Extern { | |
| ext = 1 | |
| } | |
| switch { | |
| case r.Scattered: | |
| ri.Addr = r.Addr&(1<<24-1) | typ<<24 | len<<28 | 1<<31 | pcrel<<30 | |
| ri.Symnum = r.Value | |
| case o == binary.LittleEndian: | |
| ri.Addr = r.Addr | |
| ri.Symnum = r.Value&(1<<24-1) | pcrel<<24 | len<<25 | ext<<27 | typ<<28 | |
| case o == binary.BigEndian: | |
| ri.Addr = r.Addr | |
| ri.Symnum = r.Value<<8 | pcrel<<7 | len<<5 | ext<<4 | typ | |
| } | |
| o.PutUint32(b, ri.Addr) | |
| o.PutUint32(b[4:], ri.Symnum) | |
| a += 8 | |
| b = b[8:] | |
| } | |
| return a | |
| } | |
| func putAtMost16Bytes(b []byte, n string) { | |
| for i := range n { // at most 16 bytes | |
| if i == 16 { | |
| break | |
| } | |
| b[i] = n[i] | |
| } | |
| } | |
| // A Symbol is a Mach-O 32-bit or 64-bit symbol table entry. | |
| type Symbol struct { | |
| Name string | |
| Type uint8 | |
| Sect uint8 | |
| Desc uint16 | |
| Value uint64 | |
| } | |
| /* | |
| * Mach-O reader | |
| */ | |
| // FormatError is returned by some operations if the data does | |
| // not have the correct format for an object file. | |
| type FormatError struct { | |
| off int64 | |
| msg string | |
| } | |
| func formatError(off int64, format string, data ...interface{}) *FormatError { | |
| return &FormatError{off, fmt.Sprintf(format, data...)} | |
| } | |
| func (e *FormatError) Error() string { | |
| return e.msg + fmt.Sprintf(" in record at byte %#x", e.off) | |
| } | |
| func (e *FormatError) String() string { | |
| return e.Error() | |
| } | |
| // DerivedCopy returns a modified copy of the TOC, with empty loads and sections, | |
| // and with the specified header type and flags. | |
| func (t *FileTOC) DerivedCopy(Type HdrType, Flags HdrFlags) *FileTOC { | |
| h := t.FileHeader | |
| h.NCommands, h.SizeCommands, h.Type, h.Flags = 0, 0, Type, Flags | |
| return &FileTOC{FileHeader: h, ByteOrder: t.ByteOrder} | |
| } | |
| // TOCSize returns the size in bytes of the object file representation | |
| // of the header and Load Commands (including Segments and Sections, but | |
| // not their contents) at the beginning of a Mach-O file. This typically | |
| // overlaps the text segment in the object file. | |
| func (t *FileTOC) TOCSize() uint32 { | |
| return t.HdrSize() + t.LoadSize() | |
| } | |
| // LoadAlign returns the required alignment of Load commands in a binary. | |
| // This is used to add padding for necessary alignment. | |
| func (t *FileTOC) LoadAlign() uint64 { | |
| if t.Magic == Magic64 { | |
| return 8 | |
| } | |
| return 4 | |
| } | |
| // SymbolSize returns the size in bytes of a Symbol (Nlist32 or Nlist64) | |
| func (t *FileTOC) SymbolSize() uint32 { | |
| if t.Magic == Magic64 { | |
| return uint32(unsafe.Sizeof(Nlist64{})) | |
| } | |
| return uint32(unsafe.Sizeof(Nlist32{})) | |
| } | |
| // HdrSize returns the size in bytes of the Macho header for a given | |
| // magic number (where the magic number has been appropriately byte-swapped). | |
| func (t *FileTOC) HdrSize() uint32 { | |
| switch t.Magic { | |
| case Magic32: | |
| return fileHeaderSize32 | |
| case Magic64: | |
| return fileHeaderSize64 | |
| case MagicFat: | |
| panic("MagicFat not handled yet") | |
| default: | |
| panic(fmt.Sprintf("Unexpected magic number 0x%x, expected Mach-O object file", t.Magic)) | |
| } | |
| } | |
| // LoadSize returns the size of all the load commands in a file's table-of contents | |
| // (but not their associated data, e.g., sections and symbol tables) | |
| func (t *FileTOC) LoadSize() uint32 { | |
| cmdsz := uint32(0) | |
| for _, l := range t.Loads { | |
| s := l.LoadSize(t) | |
| cmdsz += s | |
| } | |
| return cmdsz | |
| } | |
| // FileSize returns the size in bytes of the header, load commands, and the | |
| // in-file contents of all the segments and sections included in those | |
| // load commands, accounting for their offsets within the file. | |
| func (t *FileTOC) FileSize() uint64 { | |
| sz := uint64(t.LoadSize()) // ought to be contained in text segment, but just in case. | |
| for _, l := range t.Loads { | |
| if s, ok := l.(*Segment); ok { | |
| if m := s.Offset + s.Filesz; m > sz { | |
| sz = m | |
| } | |
| } | |
| } | |
| return sz | |
| } | |
| // Put writes the header and all load commands to buffer, using | |
| // the byte ordering specified in FileTOC t. For sections, this | |
| // writes the headers that come in-line with the segment Load commands, | |
| // but does not write the reference data for those sections. | |
| func (t *FileTOC) Put(buffer []byte) int { | |
| next := t.FileHeader.Put(buffer, t.ByteOrder) | |
| for _, l := range t.Loads { | |
| if s, ok := l.(*Segment); ok { | |
| switch t.Magic { | |
| case Magic64: | |
| next += s.Put64(buffer[next:], t.ByteOrder) | |
| for i := uint32(0); i < s.Nsect; i++ { | |
| c := t.Sections[i+s.Firstsect] | |
| next += c.Put64(buffer[next:], t.ByteOrder) | |
| } | |
| case Magic32: | |
| next += s.Put32(buffer[next:], t.ByteOrder) | |
| for i := uint32(0); i < s.Nsect; i++ { | |
| c := t.Sections[i+s.Firstsect] | |
| next += c.Put32(buffer[next:], t.ByteOrder) | |
| } | |
| default: | |
| panic(fmt.Sprintf("Unexpected magic number 0x%x", t.Magic)) | |
| } | |
| } else { | |
| next += l.Put(buffer[next:], t.ByteOrder) | |
| } | |
| } | |
| return next | |
| } | |
| // UncompressedSize returns the size of the segment with its sections uncompressed, ignoring | |
| // its offset within the file. The returned size is rounded up to the power of two in align. | |
| func (s *Segment) UncompressedSize(t *FileTOC, align uint64) uint64 { | |
| sz := uint64(0) | |
| for j := uint32(0); j < s.Nsect; j++ { | |
| c := t.Sections[j+s.Firstsect] | |
| sz += c.UncompressedSize() | |
| } | |
| return (sz + align - 1) & uint64(-int64(align)) | |
| } | |
| func (s *Section) UncompressedSize() uint64 { | |
| if !strings.HasPrefix(s.Name, "__z") { | |
| return s.Size | |
| } | |
| b := make([]byte, 12) | |
| n, err := s.sr.ReadAt(b, 0) | |
| if err != nil { | |
| panic("Malformed object file") | |
| } | |
| if n != len(b) { | |
| return s.Size | |
| } | |
| if string(b[:4]) == "ZLIB" { | |
| return binary.BigEndian.Uint64(b[4:12]) | |
| } | |
| return s.Size | |
| } | |
| func (s *Section) PutData(b []byte) { | |
| bb := b[0:s.Size] | |
| n, err := s.sr.ReadAt(bb, 0) | |
| if err != nil || uint64(n) != s.Size { | |
| panic("Malformed object file (ReadAt error)") | |
| } | |
| } | |
| func (s *Section) PutUncompressedData(b []byte) { | |
| if strings.HasPrefix(s.Name, "__z") { | |
| bb := make([]byte, 12) | |
| n, err := s.sr.ReadAt(bb, 0) | |
| if err != nil { | |
| panic("Malformed object file") | |
| } | |
| if n == len(bb) && string(bb[:4]) == "ZLIB" { | |
| size := binary.BigEndian.Uint64(bb[4:12]) | |
| // Decompress starting at b[12:] | |
| r, err := zlib.NewReader(io.NewSectionReader(s, 12, int64(size)-12)) | |
| if err != nil { | |
| panic("Malformed object file (zlib.NewReader error)") | |
| } | |
| n, err := io.ReadFull(r, b[0:size]) | |
| if err != nil { | |
| panic("Malformed object file (ReadFull error)") | |
| } | |
| if uint64(n) != size { | |
| panic(fmt.Sprintf("PutUncompressedData, expected to read %d bytes, instead read %d", size, n)) | |
| } | |
| if err := r.Close(); err != nil { | |
| panic("Malformed object file (Close error)") | |
| } | |
| return | |
| } | |
| } | |
| // Not compressed | |
| s.PutData(b) | |
| } | |
| func (b LoadBytes) String() string { | |
| s := "[" | |
| for i, a := range b { | |
| if i > 0 { | |
| s += " " | |
| if len(b) > 48 && i >= 16 { | |
| s += fmt.Sprintf("... (%d bytes)", len(b)) | |
| break | |
| } | |
| } | |
| s += fmt.Sprintf("%x", a) | |
| } | |
| s += "]" | |
| return s | |
| } | |
| func (b LoadBytes) Raw() []byte { return b } | |
| func (b LoadBytes) Copy() LoadBytes { return LoadBytes(append([]byte{}, b...)) } | |
| func (b LoadBytes) LoadSize(t *FileTOC) uint32 { return uint32(len(b)) } | |
| func (lc LoadCmd) Put(b []byte, o binary.ByteOrder) int { | |
| panic(fmt.Sprintf("Put not implemented for %s", lc.String())) | |
| } | |
| func (s LoadCmdBytes) String() string { | |
| return s.LoadCmd.String() + ": " + s.LoadBytes.String() | |
| } | |
| func (s LoadCmdBytes) Copy() LoadCmdBytes { | |
| return LoadCmdBytes{LoadCmd: s.LoadCmd, LoadBytes: s.LoadBytes.Copy()} | |
| } | |
| func (s *SegmentHeader) String() string { | |
| return fmt.Sprintf( | |
| "Seg %s, len=0x%x, addr=0x%x, memsz=0x%x, offset=0x%x, filesz=0x%x, maxprot=0x%x, prot=0x%x, nsect=%d, flag=0x%x, firstsect=%d", | |
| s.Name, s.Len, s.Addr, s.Memsz, s.Offset, s.Filesz, s.Maxprot, s.Prot, s.Nsect, s.Flag, s.Firstsect) | |
| } | |
| func (s *Segment) String() string { | |
| return fmt.Sprintf( | |
| "Seg %s, len=0x%x, addr=0x%x, memsz=0x%x, offset=0x%x, filesz=0x%x, maxprot=0x%x, prot=0x%x, nsect=%d, flag=0x%x, firstsect=%d", | |
| s.Name, s.Len, s.Addr, s.Memsz, s.Offset, s.Filesz, s.Maxprot, s.Prot, s.Nsect, s.Flag, s.Firstsect) | |
| } | |
| // Data reads and returns the contents of the segment. | |
| func (s *Segment) Data() ([]byte, error) { | |
| dat := make([]byte, s.sr.Size()) | |
| n, err := s.sr.ReadAt(dat, 0) | |
| if n == len(dat) { | |
| err = nil | |
| } | |
| return dat[0:n], err | |
| } | |
| func (s *Segment) Copy() *Segment { | |
| r := &Segment{SegmentHeader: s.SegmentHeader} | |
| return r | |
| } | |
| func (s *Segment) CopyZeroed() *Segment { | |
| r := s.Copy() | |
| r.Filesz = 0 | |
| r.Offset = 0 | |
| r.Nsect = 0 | |
| r.Firstsect = 0 | |
| if s.Command() == LcSegment64 { | |
| r.Len = uint32(unsafe.Sizeof(Segment64{})) | |
| } else { | |
| r.Len = uint32(unsafe.Sizeof(Segment32{})) | |
| } | |
| return r | |
| } | |
| func (s *Segment) LoadSize(t *FileTOC) uint32 { | |
| if s.Command() == LcSegment64 { | |
| return uint32(unsafe.Sizeof(Segment64{})) + uint32(s.Nsect)*uint32(unsafe.Sizeof(Section64{})) | |
| } | |
| return uint32(unsafe.Sizeof(Segment32{})) + uint32(s.Nsect)*uint32(unsafe.Sizeof(Section32{})) | |
| } | |
| // Open returns a new ReadSeeker reading the segment. | |
| func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } | |
| // Data reads and returns the contents of the Mach-O section. | |
| func (s *Section) Data() ([]byte, error) { | |
| dat := make([]byte, s.sr.Size()) | |
| n, err := s.sr.ReadAt(dat, 0) | |
| if n == len(dat) { | |
| err = nil | |
| } | |
| return dat[0:n], err | |
| } | |
| func (s *Section) Copy() *Section { | |
| return &Section{SectionHeader: s.SectionHeader} | |
| } | |
| // Open returns a new ReadSeeker reading the Mach-O section. | |
| func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } | |
| // A Dylib represents a Mach-O load dynamic library command. | |
| type Dylib struct { | |
| DylibCmd | |
| Name string | |
| Time uint32 | |
| CurrentVersion uint32 | |
| CompatVersion uint32 | |
| } | |
| func (s *Dylib) String() string { return "Dylib " + s.Name } | |
| func (s *Dylib) Copy() *Dylib { | |
| r := *s | |
| return &r | |
| } | |
| func (s *Dylib) LoadSize(t *FileTOC) uint32 { | |
| return uint32(RoundUp(uint64(unsafe.Sizeof(DylibCmd{}))+uint64(len(s.Name)), t.LoadAlign())) | |
| } | |
| type Dylinker struct { | |
| DylinkerCmd // shared by 3 commands, need the LoadCmd | |
| Name string | |
| } | |
| func (s *Dylinker) String() string { return s.DylinkerCmd.LoadCmd.String() + " " + s.Name } | |
| func (s *Dylinker) Copy() *Dylinker { | |
| return &Dylinker{DylinkerCmd: s.DylinkerCmd, Name: s.Name} | |
| } | |
| func (s *Dylinker) LoadSize(t *FileTOC) uint32 { | |
| return uint32(RoundUp(uint64(unsafe.Sizeof(DylinkerCmd{}))+uint64(len(s.Name)), t.LoadAlign())) | |
| } | |
| // A Symtab represents a Mach-O symbol table command. | |
| type Symtab struct { | |
| SymtabCmd | |
| Syms []Symbol | |
| } | |
| func (s *Symtab) Put(b []byte, o binary.ByteOrder) int { | |
| o.PutUint32(b[0*4:], uint32(s.LoadCmd)) | |
| o.PutUint32(b[1*4:], s.Len) | |
| o.PutUint32(b[2*4:], s.Symoff) | |
| o.PutUint32(b[3*4:], s.Nsyms) | |
| o.PutUint32(b[4*4:], s.Stroff) | |
| o.PutUint32(b[5*4:], s.Strsize) | |
| return 6 * 4 | |
| } | |
| func (s *Symtab) String() string { return fmt.Sprintf("Symtab %#v", s.SymtabCmd) } | |
| func (s *Symtab) Copy() *Symtab { | |
| return &Symtab{SymtabCmd: s.SymtabCmd, Syms: append([]Symbol{}, s.Syms...)} | |
| } | |
| func (s *Symtab) LoadSize(t *FileTOC) uint32 { | |
| return uint32(unsafe.Sizeof(SymtabCmd{})) | |
| } | |
| type LinkEditData struct { | |
| LinkEditDataCmd | |
| } | |
| func (s *LinkEditData) String() string { return "LinkEditData " + s.LoadCmd.String() } | |
| func (s *LinkEditData) Copy() *LinkEditData { | |
| return &LinkEditData{LinkEditDataCmd: s.LinkEditDataCmd} | |
| } | |
| func (s *LinkEditData) LoadSize(t *FileTOC) uint32 { | |
| return uint32(unsafe.Sizeof(LinkEditDataCmd{})) | |
| } | |
| type Uuid struct { | |
| UuidCmd | |
| } | |
| func (s *Uuid) String() string { | |
| return fmt.Sprintf("Uuid %X-%X-%X-%X-%X", | |
| s.Id[0:4], s.Id[4:6], s.Id[6:8], s.Id[8:10], s.Id[10:16]) | |
| } // 8-4-4-4-12 | |
| func (s *Uuid) Copy() *Uuid { | |
| return &Uuid{UuidCmd: s.UuidCmd} | |
| } | |
| func (s *Uuid) LoadSize(t *FileTOC) uint32 { | |
| return uint32(unsafe.Sizeof(UuidCmd{})) | |
| } | |
| func (s *Uuid) Put(b []byte, o binary.ByteOrder) int { | |
| o.PutUint32(b[0*4:], uint32(s.LoadCmd)) | |
| o.PutUint32(b[1*4:], s.Len) | |
| copy(b[2*4:], s.Id[0:]) | |
| return int(s.Len) | |
| } | |
| type DyldInfo struct { | |
| DyldInfoCmd | |
| } | |
| func (s *DyldInfo) String() string { return "DyldInfo " + s.LoadCmd.String() } | |
| func (s *DyldInfo) Copy() *DyldInfo { | |
| return &DyldInfo{DyldInfoCmd: s.DyldInfoCmd} | |
| } | |
| func (s *DyldInfo) LoadSize(t *FileTOC) uint32 { | |
| return uint32(unsafe.Sizeof(DyldInfoCmd{})) | |
| } | |
| type EncryptionInfo struct { | |
| EncryptionInfoCmd | |
| } | |
| func (s *EncryptionInfo) String() string { return "EncryptionInfo " + s.LoadCmd.String() } | |
| func (s *EncryptionInfo) Copy() *EncryptionInfo { | |
| return &EncryptionInfo{EncryptionInfoCmd: s.EncryptionInfoCmd} | |
| } | |
| func (s *EncryptionInfo) LoadSize(t *FileTOC) uint32 { | |
| return uint32(unsafe.Sizeof(EncryptionInfoCmd{})) | |
| } | |
| // A Dysymtab represents a Mach-O dynamic symbol table command. | |
| type Dysymtab struct { | |
| DysymtabCmd | |
| IndirectSyms []uint32 // indices into Symtab.Syms | |
| } | |
| func (s *Dysymtab) String() string { return fmt.Sprintf("Dysymtab %#v", s.DysymtabCmd) } | |
| func (s *Dysymtab) Copy() *Dysymtab { | |
| return &Dysymtab{DysymtabCmd: s.DysymtabCmd, IndirectSyms: append([]uint32{}, s.IndirectSyms...)} | |
| } | |
| func (s *Dysymtab) LoadSize(t *FileTOC) uint32 { | |
| return uint32(unsafe.Sizeof(DysymtabCmd{})) | |
| } | |
| // A Rpath represents a Mach-O rpath command. | |
| type Rpath struct { | |
| LoadCmd | |
| Path string | |
| } | |
| func (s *Rpath) String() string { return "Rpath " + s.Path } | |
| func (s *Rpath) Command() LoadCmd { return LcRpath } | |
| func (s *Rpath) Copy() *Rpath { | |
| return &Rpath{Path: s.Path} | |
| } | |
| func (s *Rpath) LoadSize(t *FileTOC) uint32 { | |
| return uint32(RoundUp(uint64(unsafe.Sizeof(RpathCmd{}))+uint64(len(s.Path)), t.LoadAlign())) | |
| } | |
| // Open opens the named file using os.Open and prepares it for use as a Mach-O binary. | |
| func Open(name string) (*File, error) { | |
| f, err := os.Open(name) | |
| if err != nil { | |
| return nil, err | |
| } | |
| ff, err := NewFile(f) | |
| if err != nil { | |
| f.Close() | |
| return nil, err | |
| } | |
| ff.closer = f | |
| return ff, nil | |
| } | |
| // Close closes the File. | |
| // If the File was created using NewFile directly instead of Open, | |
| // Close has no effect. | |
| func (f *File) Close() error { | |
| var err error | |
| if f.closer != nil { | |
| err = f.closer.Close() | |
| f.closer = nil | |
| } | |
| return err | |
| } | |
| // NewFile creates a new File for accessing a Mach-O binary in an underlying reader. | |
| // The Mach-O binary is expected to start at position 0 in the ReaderAt. | |
| func NewFile(r io.ReaderAt) (*File, error) { | |
| f := new(File) | |
| sr := io.NewSectionReader(r, 0, 1<<63-1) | |
| // Read and decode Mach magic to determine byte order, size. | |
| // Magic32 and Magic64 differ only in the bottom bit. | |
| var ident [4]byte | |
| if _, err := r.ReadAt(ident[0:], 0); err != nil { | |
| return nil, err | |
| } | |
| be := binary.BigEndian.Uint32(ident[0:]) | |
| le := binary.LittleEndian.Uint32(ident[0:]) | |
| switch Magic32 &^ 1 { | |
| case be &^ 1: | |
| f.ByteOrder = binary.BigEndian | |
| f.Magic = be | |
| case le &^ 1: | |
| f.ByteOrder = binary.LittleEndian | |
| f.Magic = le | |
| default: | |
| return nil, formatError(0, "invalid magic number be=0x%x, le=0x%x", be, le) | |
| } | |
| // Read entire file header. | |
| if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil { | |
| return nil, err | |
| } | |
| // Then load commands. | |
| offset := int64(fileHeaderSize32) | |
| if f.Magic == Magic64 { | |
| offset = fileHeaderSize64 | |
| } | |
| dat := make([]byte, f.SizeCommands) | |
| if _, err := r.ReadAt(dat, offset); err != nil { | |
| return nil, err | |
| } | |
| f.Loads = make([]Load, f.NCommands) | |
| bo := f.ByteOrder | |
| for i := range f.Loads { | |
| // Each load command begins with uint32 command and length. | |
| if len(dat) < 8 { | |
| return nil, formatError(offset, "command block too small, len(dat) = %d", len(dat)) | |
| } | |
| cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8]) | |
| if siz < 8 || siz > uint32(len(dat)) { | |
| return nil, formatError(offset, "invalid command block size, len(dat)=%d, size=%d", len(dat), siz) | |
| } | |
| var cmddat []byte | |
| cmddat, dat = dat[0:siz], dat[siz:] | |
| offset += int64(siz) | |
| var s *Segment | |
| switch cmd { | |
| default: | |
| f.Loads[i] = LoadCmdBytes{LoadCmd(cmd), LoadBytes(cmddat)} | |
| case LcUuid: | |
| var hdr UuidCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| l := &Uuid{UuidCmd: hdr} | |
| f.Loads[i] = l | |
| case LcRpath: | |
| var hdr RpathCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| l := &Rpath{LoadCmd: hdr.LoadCmd} | |
| if hdr.Path >= uint32(len(cmddat)) { | |
| return nil, formatError(offset, "invalid path in rpath command, len(cmddat)=%d, hdr.Path=%d", len(cmddat), hdr.Path) | |
| } | |
| l.Path = cstring(cmddat[hdr.Path:]) | |
| f.Loads[i] = l | |
| case LcLoadDylinker, LcIdDylinker, LcDyldEnvironment: | |
| var hdr DylinkerCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| l := new(Dylinker) | |
| if hdr.Name >= uint32(len(cmddat)) { | |
| return nil, formatError(offset, "invalid name in dynamic linker command, hdr.Name=%d, len(cmddat)=%d", hdr.Name, len(cmddat)) | |
| } | |
| l.Name = cstring(cmddat[hdr.Name:]) | |
| l.DylinkerCmd = hdr | |
| f.Loads[i] = l | |
| case LcDylib: | |
| var hdr DylibCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| l := new(Dylib) | |
| if hdr.Name >= uint32(len(cmddat)) { | |
| return nil, formatError(offset, "invalid name in dynamic library command, hdr.Name=%d, len(cmddat)=%d", hdr.Name, len(cmddat)) | |
| } | |
| l.Name = cstring(cmddat[hdr.Name:]) | |
| l.Time = hdr.Time | |
| l.CurrentVersion = hdr.CurrentVersion | |
| l.CompatVersion = hdr.CompatVersion | |
| f.Loads[i] = l | |
| case LcSymtab: | |
| var hdr SymtabCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| strtab := make([]byte, hdr.Strsize) | |
| if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil { | |
| return nil, err | |
| } | |
| var symsz int | |
| if f.Magic == Magic64 { | |
| symsz = 16 | |
| } else { | |
| symsz = 12 | |
| } | |
| symdat := make([]byte, int(hdr.Nsyms)*symsz) | |
| if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil { | |
| return nil, err | |
| } | |
| st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset) | |
| st.SymtabCmd = hdr | |
| if err != nil { | |
| return nil, err | |
| } | |
| f.Loads[i] = st | |
| f.Symtab = st | |
| case LcDysymtab: | |
| var hdr DysymtabCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| dat := make([]byte, hdr.Nindirectsyms*4) | |
| if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil { | |
| return nil, err | |
| } | |
| x := make([]uint32, hdr.Nindirectsyms) | |
| if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil { | |
| return nil, err | |
| } | |
| st := new(Dysymtab) | |
| st.DysymtabCmd = hdr | |
| st.IndirectSyms = x | |
| f.Loads[i] = st | |
| f.Dysymtab = st | |
| case LcSegment: | |
| var seg32 Segment32 | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &seg32); err != nil { | |
| return nil, err | |
| } | |
| s = new(Segment) | |
| s.LoadCmd = cmd | |
| s.Len = siz | |
| s.Name = cstring(seg32.Name[0:]) | |
| s.Addr = uint64(seg32.Addr) | |
| s.Memsz = uint64(seg32.Memsz) | |
| s.Offset = uint64(seg32.Offset) | |
| s.Filesz = uint64(seg32.Filesz) | |
| s.Maxprot = seg32.Maxprot | |
| s.Prot = seg32.Prot | |
| s.Nsect = seg32.Nsect | |
| s.Flag = seg32.Flag | |
| s.Firstsect = uint32(len(f.Sections)) | |
| f.Loads[i] = s | |
| for i := 0; i < int(s.Nsect); i++ { | |
| var sh32 Section32 | |
| if err := binary.Read(b, bo, &sh32); err != nil { | |
| return nil, err | |
| } | |
| sh := new(Section) | |
| sh.Name = cstring(sh32.Name[0:]) | |
| sh.Seg = cstring(sh32.Seg[0:]) | |
| sh.Addr = uint64(sh32.Addr) | |
| sh.Size = uint64(sh32.Size) | |
| sh.Offset = sh32.Offset | |
| sh.Align = sh32.Align | |
| sh.Reloff = sh32.Reloff | |
| sh.Nreloc = sh32.Nreloc | |
| sh.Flags = sh32.Flags | |
| sh.Reserved1 = sh32.Reserve1 | |
| sh.Reserved2 = sh32.Reserve2 | |
| if err := f.pushSection(sh, r); err != nil { | |
| return nil, err | |
| } | |
| } | |
| case LcSegment64: | |
| var seg64 Segment64 | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &seg64); err != nil { | |
| return nil, err | |
| } | |
| s = new(Segment) | |
| s.LoadCmd = cmd | |
| s.Len = siz | |
| s.Name = cstring(seg64.Name[0:]) | |
| s.Addr = seg64.Addr | |
| s.Memsz = seg64.Memsz | |
| s.Offset = seg64.Offset | |
| s.Filesz = seg64.Filesz | |
| s.Maxprot = seg64.Maxprot | |
| s.Prot = seg64.Prot | |
| s.Nsect = seg64.Nsect | |
| s.Flag = seg64.Flag | |
| s.Firstsect = uint32(len(f.Sections)) | |
| f.Loads[i] = s | |
| for i := 0; i < int(s.Nsect); i++ { | |
| var sh64 Section64 | |
| if err := binary.Read(b, bo, &sh64); err != nil { | |
| return nil, err | |
| } | |
| sh := new(Section) | |
| sh.Name = cstring(sh64.Name[0:]) | |
| sh.Seg = cstring(sh64.Seg[0:]) | |
| sh.Addr = sh64.Addr | |
| sh.Size = sh64.Size | |
| sh.Offset = sh64.Offset | |
| sh.Align = sh64.Align | |
| sh.Reloff = sh64.Reloff | |
| sh.Nreloc = sh64.Nreloc | |
| sh.Flags = sh64.Flags | |
| sh.Reserved1 = sh64.Reserve1 | |
| sh.Reserved2 = sh64.Reserve2 | |
| sh.Reserved3 = sh64.Reserve3 | |
| if err := f.pushSection(sh, r); err != nil { | |
| return nil, err | |
| } | |
| } | |
| case LcCodeSignature, LcSegmentSplitInfo, LcFunctionStarts, | |
| LcDataInCode, LcDylibCodeSignDrs: | |
| var hdr LinkEditDataCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| l := new(LinkEditData) | |
| l.LinkEditDataCmd = hdr | |
| f.Loads[i] = l | |
| case LcEncryptionInfo, LcEncryptionInfo64: | |
| var hdr EncryptionInfoCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| l := new(EncryptionInfo) | |
| l.EncryptionInfoCmd = hdr | |
| f.Loads[i] = l | |
| case LcDyldInfo, LcDyldInfoOnly: | |
| var hdr DyldInfoCmd | |
| b := bytes.NewReader(cmddat) | |
| if err := binary.Read(b, bo, &hdr); err != nil { | |
| return nil, err | |
| } | |
| l := new(DyldInfo) | |
| l.DyldInfoCmd = hdr | |
| f.Loads[i] = l | |
| } | |
| if s != nil { | |
| s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz)) | |
| s.ReaderAt = s.sr | |
| } | |
| if f.Loads[i].LoadSize(&f.FileTOC) != siz { | |
| fmt.Printf("Oops, actual size was %d, calculated was %d, load was %s\n", siz, f.Loads[i].LoadSize(&f.FileTOC), f.Loads[i].String()) | |
| panic("oops") | |
| } | |
| } | |
| return f, nil | |
| } | |
| func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) { | |
| bo := f.ByteOrder | |
| symtab := make([]Symbol, hdr.Nsyms) | |
| b := bytes.NewReader(symdat) | |
| for i := range symtab { | |
| var n Nlist64 | |
| if f.Magic == Magic64 { | |
| if err := binary.Read(b, bo, &n); err != nil { | |
| return nil, err | |
| } | |
| } else { | |
| var n32 Nlist32 | |
| if err := binary.Read(b, bo, &n32); err != nil { | |
| return nil, err | |
| } | |
| n.Name = n32.Name | |
| n.Type = n32.Type | |
| n.Sect = n32.Sect | |
| n.Desc = n32.Desc | |
| n.Value = uint64(n32.Value) | |
| } | |
| sym := &symtab[i] | |
| if n.Name >= uint32(len(strtab)) { | |
| return nil, formatError(offset, "invalid name in symbol table, n.Name=%d, len(strtab)=%d", n.Name, len(strtab)) | |
| } | |
| sym.Name = cstring(strtab[n.Name:]) | |
| sym.Type = n.Type | |
| sym.Sect = n.Sect | |
| sym.Desc = n.Desc | |
| sym.Value = n.Value | |
| } | |
| st := new(Symtab) | |
| st.Syms = symtab | |
| return st, nil | |
| } | |
| type relocInfo struct { | |
| Addr uint32 | |
| Symnum uint32 | |
| } | |
| func (f *File) pushSection(sh *Section, r io.ReaderAt) error { | |
| f.Sections = append(f.Sections, sh) | |
| sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) | |
| sh.ReaderAt = sh.sr | |
| if sh.Nreloc > 0 { | |
| reldat := make([]byte, int(sh.Nreloc)*8) | |
| if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil { | |
| return err | |
| } | |
| b := bytes.NewReader(reldat) | |
| bo := f.ByteOrder | |
| sh.Relocs = make([]Reloc, sh.Nreloc) | |
| for i := range sh.Relocs { | |
| rel := &sh.Relocs[i] | |
| var ri relocInfo | |
| if err := binary.Read(b, bo, &ri); err != nil { | |
| return err | |
| } | |
| if ri.Addr&(1<<31) != 0 { // scattered | |
| rel.Addr = ri.Addr & (1<<24 - 1) | |
| rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1)) | |
| rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1)) | |
| rel.Pcrel = ri.Addr&(1<<30) != 0 | |
| rel.Value = ri.Symnum | |
| rel.Scattered = true | |
| } else { | |
| switch bo { | |
| case binary.LittleEndian: | |
| rel.Addr = ri.Addr | |
| rel.Value = ri.Symnum & (1<<24 - 1) | |
| rel.Pcrel = ri.Symnum&(1<<24) != 0 | |
| rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1)) | |
| rel.Extern = ri.Symnum&(1<<27) != 0 | |
| rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1)) | |
| case binary.BigEndian: | |
| rel.Addr = ri.Addr | |
| rel.Value = ri.Symnum >> 8 | |
| rel.Pcrel = ri.Symnum&(1<<7) != 0 | |
| rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1)) | |
| rel.Extern = ri.Symnum&(1<<4) != 0 | |
| rel.Type = uint8(ri.Symnum & (1<<4 - 1)) | |
| default: | |
| panic("unreachable") | |
| } | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| func cstring(b []byte) string { | |
| i := bytes.IndexByte(b, 0) | |
| if i == -1 { | |
| i = len(b) | |
| } | |
| return string(b[0:i]) | |
| } | |
| // Segment returns the first Segment with the given name, or nil if no such segment exists. | |
| func (f *File) Segment(name string) *Segment { | |
| for _, l := range f.Loads { | |
| if s, ok := l.(*Segment); ok && s.Name == name { | |
| return s | |
| } | |
| } | |
| return nil | |
| } | |
| // Section returns the first section with the given name, or nil if no such | |
| // section exists. | |
| func (f *File) Section(name string) *Section { | |
| for _, s := range f.Sections { | |
| if s.Name == name { | |
| return s | |
| } | |
| } | |
| return nil | |
| } | |
| // DWARF returns the DWARF debug information for the Mach-O file. | |
| func (f *File) DWARF() (*dwarf.Data, error) { | |
| dwarfSuffix := func(s *Section) string { | |
| switch { | |
| case strings.HasPrefix(s.Name, "__debug_"): | |
| return s.Name[8:] | |
| case strings.HasPrefix(s.Name, "__zdebug_"): | |
| return s.Name[9:] | |
| default: | |
| return "" | |
| } | |
| } | |
| sectionData := func(s *Section) ([]byte, error) { | |
| b, err := s.Data() | |
| if err != nil && uint64(len(b)) < s.Size { | |
| return nil, err | |
| } | |
| if len(b) >= 12 && string(b[:4]) == "ZLIB" { | |
| dlen := binary.BigEndian.Uint64(b[4:12]) | |
| dbuf := make([]byte, dlen) | |
| r, err := zlib.NewReader(bytes.NewBuffer(b[12:])) | |
| if err != nil { | |
| return nil, err | |
| } | |
| if _, err := io.ReadFull(r, dbuf); err != nil { | |
| return nil, err | |
| } | |
| if err := r.Close(); err != nil { | |
| return nil, err | |
| } | |
| b = dbuf | |
| } | |
| return b, nil | |
| } | |
| // There are many other DWARF sections, but these | |
| // are the ones the debug/dwarf package uses. | |
| // Don't bother loading others. | |
| var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} | |
| for _, s := range f.Sections { | |
| suffix := dwarfSuffix(s) | |
| if suffix == "" { | |
| continue | |
| } | |
| if _, ok := dat[suffix]; !ok { | |
| continue | |
| } | |
| b, err := sectionData(s) | |
| if err != nil { | |
| return nil, err | |
| } | |
| dat[suffix] = b | |
| } | |
| d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) | |
| if err != nil { | |
| return nil, err | |
| } | |
| // Look for DWARF4 .debug_types sections. | |
| for i, s := range f.Sections { | |
| suffix := dwarfSuffix(s) | |
| if suffix != "types" { | |
| continue | |
| } | |
| b, err := sectionData(s) | |
| if err != nil { | |
| return nil, err | |
| } | |
| err = d.AddTypes(fmt.Sprintf("types-%d", i), b) | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| return d, nil | |
| } | |
| // ImportedSymbols returns the names of all symbols | |
| // referred to by the binary f that are expected to be | |
| // satisfied by other libraries at dynamic load time. | |
| func (f *File) ImportedSymbols() ([]string, error) { | |
| if f.Dysymtab == nil || f.Symtab == nil { | |
| return nil, formatError(0, "missing symbol table, f.Dsymtab=%v, f.Symtab=%v", f.Dysymtab, f.Symtab) | |
| } | |
| st := f.Symtab | |
| dt := f.Dysymtab | |
| var all []string | |
| for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { | |
| all = append(all, s.Name) | |
| } | |
| return all, nil | |
| } | |
| // ImportedLibraries returns the paths of all libraries | |
| // referred to by the binary f that are expected to be | |
| // linked with the binary at dynamic link time. | |
| func (f *File) ImportedLibraries() ([]string, error) { | |
| var all []string | |
| for _, l := range f.Loads { | |
| if lib, ok := l.(*Dylib); ok { | |
| all = append(all, lib.Name) | |
| } | |
| } | |
| return all, nil | |
| } | |
| func RoundUp(x, align uint64) uint64 { | |
| return uint64((x + align - 1) & -align) | |
| } |