blob: ca3d50e9c6006e781886e7a420cea4af9863bf8a [file] [log] [blame]
Colin Cross5498f852018-01-03 23:39:54 -08001// Copyright 2018 Google Inc. All rights reserved.
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// http://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
Dan Willemsen2249dc82018-10-15 00:35:59 -070015package symbol_inject
Colin Cross5498f852018-01-03 23:39:54 -080016
17import (
18 "debug/macho"
Yabin Cuie79fe012022-10-27 09:56:20 -070019 "encoding/binary"
Colin Cross5498f852018-01-03 23:39:54 -080020 "fmt"
21 "io"
Yabin Cuie79fe012022-10-27 09:56:20 -070022 "os"
Yabin Cui7f5f22b2022-08-31 16:23:43 -070023 "os/exec"
Yabin Cuie79fe012022-10-27 09:56:20 -070024 "path/filepath"
Colin Crossc4a18e02018-02-23 22:43:24 -080025 "sort"
Colin Cross8673b5b2018-03-01 11:20:25 -080026 "strings"
Colin Cross5498f852018-01-03 23:39:54 -080027)
28
Colin Cross8673b5b2018-03-01 11:20:25 -080029func machoSymbolsFromFile(r io.ReaderAt) (*File, error) {
Colin Cross5498f852018-01-03 23:39:54 -080030 machoFile, err := macho.NewFile(r)
31 if err != nil {
Colin Cross8673b5b2018-03-01 11:20:25 -080032 return nil, cantParseError{err}
Colin Cross5498f852018-01-03 23:39:54 -080033 }
34
Colin Cross8673b5b2018-03-01 11:20:25 -080035 return extractMachoSymbols(machoFile)
36}
Colin Cross5498f852018-01-03 23:39:54 -080037
Colin Cross8673b5b2018-03-01 11:20:25 -080038func extractMachoSymbols(machoFile *macho.File) (*File, error) {
Colin Crossc4a18e02018-02-23 22:43:24 -080039 symbols := machoFile.Symtab.Syms
Colin Cross8673b5b2018-03-01 11:20:25 -080040 sort.SliceStable(symbols, func(i, j int) bool {
Colin Crossc4a18e02018-02-23 22:43:24 -080041 if symbols[i].Sect != symbols[j].Sect {
42 return symbols[i].Sect < symbols[j].Sect
43 }
44 return symbols[i].Value < symbols[j].Value
45 })
46
Yabin Cui7f5f22b2022-08-31 16:23:43 -070047 file := &File{IsMachoFile: true}
Colin Cross8673b5b2018-03-01 11:20:25 -080048
49 for _, section := range machoFile.Sections {
50 file.Sections = append(file.Sections, &Section{
51 Name: section.Name,
52 Addr: section.Addr,
53 Offset: uint64(section.Offset),
54 Size: section.Size,
55 })
56 }
57
Colin Crossdfce7642018-02-28 13:05:39 -080058 for _, symbol := range symbols {
Colin Cross8673b5b2018-03-01 11:20:25 -080059 if symbol.Sect > 0 {
60 section := file.Sections[symbol.Sect-1]
61 file.Symbols = append(file.Symbols, &Symbol{
62 // symbols in macho files seem to be prefixed with an underscore
63 Name: strings.TrimPrefix(symbol.Name, "_"),
64 // MachO symbol value is virtual address of the symbol, convert it to offset into the section.
65 Addr: symbol.Value - section.Addr,
66 // MachO symbols don't have size information.
67 Size: 0,
68 Section: section,
Colin Crossdfce7642018-02-28 13:05:39 -080069 })
Colin Cross5498f852018-01-03 23:39:54 -080070 }
71 }
72
Colin Cross8673b5b2018-03-01 11:20:25 -080073 return file, nil
74}
75
76func dumpMachoSymbols(r io.ReaderAt) error {
77 machoFile, err := macho.NewFile(r)
78 if err != nil {
79 return cantParseError{err}
80 }
81
82 fmt.Println("&macho.File{")
83
84 fmt.Println("\tSections: []*macho.Section{")
85 for _, section := range machoFile.Sections {
86 fmt.Printf("\t\t&macho.Section{SectionHeader: %#v},\n", section.SectionHeader)
87 }
88 fmt.Println("\t},")
89
90 fmt.Println("\tSymtab: &macho.Symtab{")
91 fmt.Println("\t\tSyms: []macho.Symbol{")
92 for _, symbol := range machoFile.Symtab.Syms {
93 fmt.Printf("\t\t\t%#v,\n", symbol)
94 }
95 fmt.Println("\t\t},")
96 fmt.Println("\t},")
97
98 fmt.Println("}")
99
100 return nil
Colin Cross5498f852018-01-03 23:39:54 -0800101}
Yabin Cui7f5f22b2022-08-31 16:23:43 -0700102
103func CodeSignMachoFile(path string) error {
Yabin Cuie79fe012022-10-27 09:56:20 -0700104 filename := filepath.Base(path)
105 cmd := exec.Command("/usr/bin/codesign", "--force", "-s", "-", "-i", filename, path)
106 if err := cmd.Run(); err != nil {
107 return err
108 }
109 return modifyCodeSignFlags(path)
110}
111
112const LC_CODE_SIGNATURE = 0x1d
113const CSSLOT_CODEDIRECTORY = 0
114
115// To make codesign not invalidated by stripping, modify codesign flags to 0x20002
116// (adhoc | linkerSigned).
117func modifyCodeSignFlags(path string) error {
118 f, err := os.OpenFile(path, os.O_RDWR, 0)
119 if err != nil {
120 return err
121 }
122 defer f.Close()
123
124 // Step 1: find code signature section.
125 machoFile, err := macho.NewFile(f)
126 if err != nil {
127 return err
128 }
129 var codeSignSectionOffset uint32 = 0
130 var codeSignSectionSize uint32 = 0
131 for _, l := range machoFile.Loads {
132 data := l.Raw()
133 cmd := machoFile.ByteOrder.Uint32(data)
134 if cmd == LC_CODE_SIGNATURE {
135 codeSignSectionOffset = machoFile.ByteOrder.Uint32(data[8:])
136 codeSignSectionSize = machoFile.ByteOrder.Uint32(data[12:])
137 }
138 }
139 if codeSignSectionOffset == 0 {
140 return fmt.Errorf("code signature section not found")
141 }
142
143 data := make([]byte, codeSignSectionSize)
144 _, err = f.ReadAt(data, int64(codeSignSectionOffset))
145 if err != nil {
146 return err
147 }
148
149 // Step 2: get flags offset.
150 blobCount := binary.BigEndian.Uint32(data[8:])
151 off := 12
152 var codeDirectoryOff uint32 = 0
153 for blobCount > 0 {
154 blobType := binary.BigEndian.Uint32(data[off:])
155 if blobType == CSSLOT_CODEDIRECTORY {
156 codeDirectoryOff = binary.BigEndian.Uint32(data[off+4:])
157 break
158 }
159 blobCount--
160 off += 8
161 }
162 if codeDirectoryOff == 0 {
163 return fmt.Errorf("no code directory in code signature section")
164 }
165 flagsOff := codeSignSectionOffset + codeDirectoryOff + 12
166
167 // Step 3: modify flags.
168 flagsData := make([]byte, 4)
169 _, err = f.ReadAt(flagsData, int64(flagsOff))
170 if err != nil {
171 return err
172 }
173 oldFlags := binary.BigEndian.Uint32(flagsData)
174 if oldFlags != 0x2 {
175 return fmt.Errorf("unexpected flags in code signature section: 0x%x", oldFlags)
176 }
177 binary.BigEndian.PutUint32(flagsData, 0x20002)
178 _, err = f.WriteAt(flagsData, int64(flagsOff))
179 return err
Yabin Cui7f5f22b2022-08-31 16:23:43 -0700180}