blob: 8afe5a009fab2972faf2e20ad2d66a6b8403177f [file] [log] [blame] [edit]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cosboot
import (
"bytes"
"debug/elf"
"fmt"
"os"
)
func elfSymbol(img []byte, symbol string) (elf.Symbol, error) {
f, err := elf.NewFile(bytes.NewReader(img))
if err != nil {
return elf.Symbol{}, err
}
defer f.Close()
var targetSym elf.Symbol
var found bool
syms, err := f.Symbols()
if err != nil {
return targetSym, fmt.Errorf("failed to read symbols: %v", err)
}
for _, s := range syms {
if s.Name == symbol {
targetSym = s
found = true
break
}
}
if !found {
return targetSym, fmt.Errorf("symbol %q not found", symbol)
}
return targetSym, nil
}
func symbolContent(img []byte, symbol elf.Symbol) ([]byte, error) {
f, err := elf.NewFile(bytes.NewReader(img))
if err != nil {
return nil, err
}
defer f.Close()
if int(symbol.Section) < 0 || int(symbol.Section) >= len(f.Sections) {
return nil, fmt.Errorf("symbol %q has invalid section index %d", symbol.Name, symbol.Section)
}
section := f.Sections[symbol.Section]
var offset uint64
if f.Type == elf.ET_REL {
offset = symbol.Value
} else {
if symbol.Value < section.Addr {
return nil, fmt.Errorf("symbol %q value (0x%x) is less than section %q address (0x%x)", symbol.Name, symbol.Value, section.Name, section.Addr)
}
offset = symbol.Value - section.Addr
}
if offset+symbol.Size > section.Size {
return nil, fmt.Errorf("symbol %q extends beyond section %q", symbol.Name, section.Name)
}
data := make([]byte, symbol.Size)
if _, err := section.ReadAt(data, int64(offset)); err != nil {
return nil, fmt.Errorf("failed to read symbol %q content: %v", symbol.Name, err)
}
return data, nil
}
func NextUKICmdLine(disk, vmlinux string) (string, error) {
bootx64, err := ReadEFIFile(disk, "/efi/boot/bootx64.efi")
if err != nil {
return "", err
}
arch, err := EFIArch(bootx64)
if err != nil {
return "", err
}
var elfImg []byte
switch arch {
case "x86_64":
elfImg, err = elfFromBZImage(bootx64)
if err != nil {
return "", err
}
case "arm64":
default:
return "", fmt.Errorf("unknown architecture: %v", arch)
}
vmlinuxData, err := os.ReadFile(vmlinux)
if err != nil {
return "", err
}
cmdlineSym, err := elfSymbol(vmlinuxData, "builtin_cmdline")
if err != nil {
return "", err
}
cmdLineContent, err := symbolContent(elfImg, cmdlineSym)
if err != nil {
return "", err
}
// Respect a null-terminated string in the compiled-in command line
var cmdLine string
n := bytes.IndexByte(cmdLineContent, 0)
if n >= 0 {
cmdLine = string(cmdLineContent[:n])
} else {
cmdLine = string(cmdLineContent)
}
return cmdLine, nil
}