Skip to content

Commit c23a5b6

Browse files
aykevldeadprogram
authored andcommitted
darwin: support -size= flag
This is just basic support. It doesn't add support for reading DWARF, because that's a bit complicated on MacOS (it isn't stored in the file itself but separately in the object files). But at least this change makes it possible to easily print executable sizes by section type like for other operating systems.
1 parent b8e4338 commit c23a5b6

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,8 @@ endif
634634
@$(MD5SUM) test.hex
635635
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
636636
GOOS=windows GOARCH=amd64 $(TINYGO) build -size short -o test.exe ./testdata/cgo
637-
GOOS=darwin GOARCH=amd64 $(TINYGO) build -o test ./testdata/cgo
638-
GOOS=darwin GOARCH=arm64 $(TINYGO) build -o test ./testdata/cgo
637+
GOOS=darwin GOARCH=amd64 $(TINYGO) build -size short -o test ./testdata/cgo
638+
GOOS=darwin GOARCH=arm64 $(TINYGO) build -size short -o test ./testdata/cgo
639639
ifneq ($(OS),Windows_NT)
640640
# TODO: this does not yet work on Windows. Somehow, unused functions are
641641
# not garbage collected.

builder/sizes.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"debug/dwarf"
66
"debug/elf"
7+
"debug/macho"
78
"debug/pe"
89
"encoding/binary"
910
"fmt"
@@ -368,6 +369,58 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
368369
})
369370
}
370371
}
372+
} else if file, err := macho.NewFile(f); err == nil {
373+
// TODO: read DWARF information. On MacOS, DWARF debug information isn't
374+
// stored in the executable but stays in the object files. The
375+
// executable does however contain the object file paths that contain
376+
// debug information.
377+
378+
// Read segments, for use while reading through sections.
379+
segments := map[string]*macho.Segment{}
380+
for _, load := range file.Loads {
381+
switch load := load.(type) {
382+
case *macho.Segment:
383+
segments[load.Name] = load
384+
}
385+
}
386+
387+
// Read MachO sections.
388+
for _, section := range file.Sections {
389+
sectionType := section.Flags & 0xff
390+
sectionFlags := section.Flags >> 8
391+
segment := segments[section.Seg]
392+
// For the constants used here, see:
393+
// https://github.com/llvm/llvm-project/blob/release/14.x/llvm/include/llvm/BinaryFormat/MachO.h
394+
if sectionFlags&0x800000 != 0 { // S_ATTR_PURE_INSTRUCTIONS
395+
// Section containing only instructions.
396+
sections = append(sections, memorySection{
397+
Address: section.Addr,
398+
Size: uint64(section.Size),
399+
Type: memoryCode,
400+
})
401+
} else if sectionType == 1 { // S_ZEROFILL
402+
// Section filled with zeroes on demand.
403+
sections = append(sections, memorySection{
404+
Address: section.Addr,
405+
Size: uint64(section.Size),
406+
Type: memoryBSS,
407+
})
408+
} else if segment.Maxprot&0b011 == 0b001 { // --r (read-only data)
409+
// Protection doesn't allow writes, so mark this section read-only.
410+
sections = append(sections, memorySection{
411+
Address: section.Addr,
412+
Size: uint64(section.Size),
413+
Type: memoryROData,
414+
})
415+
} else {
416+
// The rest is assumed to be regular data.
417+
sections = append(sections, memorySection{
418+
Address: section.Addr,
419+
Size: uint64(section.Size),
420+
Type: memoryData,
421+
})
422+
}
423+
}
371424
} else if file, err := pe.NewFile(f); err == nil {
372425
// Read DWARF information. The error is intentionally ignored.
373426
data, _ := file.DWARF()

0 commit comments

Comments
 (0)