in util/fipstools/inject_hash/inject_hash.go [171:309]
func doAppleOS(objectBytes []byte) ([]byte, []byte, error) {
object, err := macho.NewFile(bytes.NewReader(objectBytes))
if err != nil {
return nil, nil, errors.New("failed to parse object: " + err.Error())
}
// Find the __text and, optionally, __const sections.
// They are both in __TEXT segment and are unique.
var textSection, rodataSection *macho.Section
var textSectionIndex, rodataSectionIndex int
for i, section := range object.Sections {
if section.Seg == "__TEXT" && section.Name == "__text" {
textSection = section
textSectionIndex = i + 1
}
if section.Seg == "__TEXT" && section.Name == "__const" {
rodataSection = section
rodataSectionIndex = i + 1
}
}
if textSection == nil {
return nil, nil, errors.New("failed to find __text section in object")
}
// Find the starting and ending symbols for the module.
var textStart, textEnd, rodataStart, rodataEnd *uint64
symbols := object.Symtab.Syms
if symbols == nil {
return nil, nil, errors.New("failed to parse symbols: " + err.Error())
}
for _, symbol := range symbols {
var base uint64
switch int(symbol.Sect) {
case textSectionIndex:
base = textSection.Addr
case rodataSectionIndex:
if rodataSection == nil {
continue
}
base = rodataSection.Addr
default:
continue
}
if symbol.Name != "" && symbol.Name != " " && symbol.Value < base {
return nil, nil, fmt.Errorf("symbol %q at %x, which is below base of %x\n", symbol.Name, symbol.Value, base)
}
// Skip debugging symbols
//
// #define N_STAB 0xe0 /* if any of these bits set, a symbolic debugging entry */
//
// "Only symbolic debugging entries have some of the N_STAB bits set and if any of these bits are set then it is
// a symbolic debugging entry (a stab). In which case then the values of the n_type field (the entire field)
// are given in <mach-o/stab.h>"
//
// https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/nlist.h
if symbol.Type&0xe0 != 0 {
continue
}
value := symbol.Value - base
switch symbol.Name {
case "_BORINGSSL_bcm_text_start":
if textStart != nil {
return nil, nil, errors.New("duplicate start symbol found")
}
textStart = &value
case "_BORINGSSL_bcm_text_end":
if textEnd != nil {
return nil, nil, errors.New("duplicate end symbol found")
}
textEnd = &value
case "_BORINGSSL_bcm_rodata_start":
if rodataStart != nil {
return nil, nil, errors.New("duplicate rodata start symbol found")
}
rodataStart = &value
case "_BORINGSSL_bcm_rodata_end":
if rodataEnd != nil {
return nil, nil, errors.New("duplicate rodata end symbol found")
}
rodataEnd = &value
default:
continue
}
}
if textStart == nil || textEnd == nil {
return nil, nil, errors.New("could not find .text module boundaries in object")
}
if (rodataStart == nil) != (rodataSection == nil) {
return nil, nil, errors.New("rodata start marker inconsistent with rodata section presence")
}
if (rodataStart != nil) != (rodataEnd != nil) {
return nil, nil, errors.New("rodata marker presence inconsistent")
}
if max := textSection.Size; *textStart > max || *textStart > *textEnd || *textEnd > max {
return nil, nil, fmt.Errorf("invalid module __text boundaries: start: %x, end: %x, max: %x", *textStart, *textEnd, max)
}
if rodataSection != nil {
if max := rodataSection.Size; *rodataStart > max || *rodataStart > *rodataEnd || *rodataEnd > max {
return nil, nil, fmt.Errorf("invalid module __const boundaries: start: %x, end: %x, max: %x", *rodataStart, *rodataEnd, max)
}
}
// Extract the module from the __text section.
text := textSection.Open()
if _, err := text.Seek(int64(*textStart), 0); err != nil {
return nil, nil, errors.New("failed to seek to module start in __text: " + err.Error())
}
moduleText := make([]byte, *textEnd-*textStart)
if _, err := io.ReadFull(text, moduleText); err != nil {
return nil, nil, errors.New("failed to read __text: " + err.Error())
}
// Maybe extract the module's read-only data too
var moduleROData []byte
if rodataSection != nil {
rodata := rodataSection.Open()
if _, err := rodata.Seek(int64(*rodataStart), 0); err != nil {
return nil, nil, errors.New("failed to seek to module start in __const: " + err.Error())
}
moduleROData = make([]byte, *rodataEnd-*rodataStart)
if _, err := io.ReadFull(rodata, moduleROData); err != nil {
return nil, nil, errors.New("failed to read __const: " + err.Error())
}
}
return moduleText, moduleROData, nil
}