instruction.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. package asm
  2. import (
  3. "crypto/sha1"
  4. "encoding/binary"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "math"
  10. "strings"
  11. "github.com/cilium/ebpf/internal/unix"
  12. )
  13. // InstructionSize is the size of a BPF instruction in bytes
  14. const InstructionSize = 8
  15. // RawInstructionOffset is an offset in units of raw BPF instructions.
  16. type RawInstructionOffset uint64
  17. // Bytes returns the offset of an instruction in bytes.
  18. func (rio RawInstructionOffset) Bytes() uint64 {
  19. return uint64(rio) * InstructionSize
  20. }
  21. // Instruction is a single eBPF instruction.
  22. type Instruction struct {
  23. OpCode OpCode
  24. Dst Register
  25. Src Register
  26. Offset int16
  27. Constant int64
  28. Reference string
  29. Symbol string
  30. }
  31. // Sym creates a symbol.
  32. func (ins Instruction) Sym(name string) Instruction {
  33. ins.Symbol = name
  34. return ins
  35. }
  36. // Unmarshal decodes a BPF instruction.
  37. func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) {
  38. var bi bpfInstruction
  39. err := binary.Read(r, bo, &bi)
  40. if err != nil {
  41. return 0, err
  42. }
  43. ins.OpCode = bi.OpCode
  44. ins.Offset = bi.Offset
  45. ins.Constant = int64(bi.Constant)
  46. ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo)
  47. if err != nil {
  48. return 0, fmt.Errorf("can't unmarshal registers: %s", err)
  49. }
  50. if !bi.OpCode.IsDWordLoad() {
  51. return InstructionSize, nil
  52. }
  53. var bi2 bpfInstruction
  54. if err := binary.Read(r, bo, &bi2); err != nil {
  55. // No Wrap, to avoid io.EOF clash
  56. return 0, errors.New("64bit immediate is missing second half")
  57. }
  58. if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 {
  59. return 0, errors.New("64bit immediate has non-zero fields")
  60. }
  61. ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant)))
  62. return 2 * InstructionSize, nil
  63. }
  64. // Marshal encodes a BPF instruction.
  65. func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {
  66. if ins.OpCode == InvalidOpCode {
  67. return 0, errors.New("invalid opcode")
  68. }
  69. isDWordLoad := ins.OpCode.IsDWordLoad()
  70. cons := int32(ins.Constant)
  71. if isDWordLoad {
  72. // Encode least significant 32bit first for 64bit operations.
  73. cons = int32(uint32(ins.Constant))
  74. }
  75. regs, err := newBPFRegisters(ins.Dst, ins.Src, bo)
  76. if err != nil {
  77. return 0, fmt.Errorf("can't marshal registers: %s", err)
  78. }
  79. bpfi := bpfInstruction{
  80. ins.OpCode,
  81. regs,
  82. ins.Offset,
  83. cons,
  84. }
  85. if err := binary.Write(w, bo, &bpfi); err != nil {
  86. return 0, err
  87. }
  88. if !isDWordLoad {
  89. return InstructionSize, nil
  90. }
  91. bpfi = bpfInstruction{
  92. Constant: int32(ins.Constant >> 32),
  93. }
  94. if err := binary.Write(w, bo, &bpfi); err != nil {
  95. return 0, err
  96. }
  97. return 2 * InstructionSize, nil
  98. }
  99. // RewriteMapPtr changes an instruction to use a new map fd.
  100. //
  101. // Returns an error if the instruction doesn't load a map.
  102. func (ins *Instruction) RewriteMapPtr(fd int) error {
  103. if !ins.OpCode.IsDWordLoad() {
  104. return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
  105. }
  106. if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue {
  107. return errors.New("not a load from a map")
  108. }
  109. // Preserve the offset value for direct map loads.
  110. offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
  111. rawFd := uint64(uint32(fd))
  112. ins.Constant = int64(offset | rawFd)
  113. return nil
  114. }
  115. // MapPtr returns the map fd for this instruction.
  116. //
  117. // The result is undefined if the instruction is not a load from a map,
  118. // see IsLoadFromMap.
  119. func (ins *Instruction) MapPtr() int {
  120. return int(int32(uint64(ins.Constant) & math.MaxUint32))
  121. }
  122. // RewriteMapOffset changes the offset of a direct load from a map.
  123. //
  124. // Returns an error if the instruction is not a direct load.
  125. func (ins *Instruction) RewriteMapOffset(offset uint32) error {
  126. if !ins.OpCode.IsDWordLoad() {
  127. return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
  128. }
  129. if ins.Src != PseudoMapValue {
  130. return errors.New("not a direct load from a map")
  131. }
  132. fd := uint64(ins.Constant) & math.MaxUint32
  133. ins.Constant = int64(uint64(offset)<<32 | fd)
  134. return nil
  135. }
  136. func (ins *Instruction) mapOffset() uint32 {
  137. return uint32(uint64(ins.Constant) >> 32)
  138. }
  139. // IsLoadFromMap returns true if the instruction loads from a map.
  140. //
  141. // This covers both loading the map pointer and direct map value loads.
  142. func (ins *Instruction) IsLoadFromMap() bool {
  143. return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
  144. }
  145. // IsFunctionCall returns true if the instruction calls another BPF function.
  146. //
  147. // This is not the same thing as a BPF helper call.
  148. func (ins *Instruction) IsFunctionCall() bool {
  149. return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
  150. }
  151. // IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call.
  152. func (ins *Instruction) IsBuiltinCall() bool {
  153. return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0
  154. }
  155. // IsConstantLoad returns true if the instruction loads a constant of the
  156. // given size.
  157. func (ins *Instruction) IsConstantLoad(size Size) bool {
  158. return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0
  159. }
  160. // Format implements fmt.Formatter.
  161. func (ins Instruction) Format(f fmt.State, c rune) {
  162. if c != 'v' {
  163. fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c)
  164. return
  165. }
  166. op := ins.OpCode
  167. if op == InvalidOpCode {
  168. fmt.Fprint(f, "INVALID")
  169. return
  170. }
  171. // Omit trailing space for Exit
  172. if op.JumpOp() == Exit {
  173. fmt.Fprint(f, op)
  174. return
  175. }
  176. if ins.IsLoadFromMap() {
  177. fd := ins.MapPtr()
  178. switch ins.Src {
  179. case PseudoMapFD:
  180. fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd)
  181. case PseudoMapValue:
  182. fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset())
  183. }
  184. goto ref
  185. }
  186. fmt.Fprintf(f, "%v ", op)
  187. switch cls := op.Class(); cls {
  188. case LdClass, LdXClass, StClass, StXClass:
  189. switch op.Mode() {
  190. case ImmMode:
  191. fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant)
  192. case AbsMode:
  193. fmt.Fprintf(f, "imm: %d", ins.Constant)
  194. case IndMode:
  195. fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant)
  196. case MemMode:
  197. fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant)
  198. case XAddMode:
  199. fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src)
  200. }
  201. case ALU64Class, ALUClass:
  202. fmt.Fprintf(f, "dst: %s ", ins.Dst)
  203. if op.ALUOp() == Swap || op.Source() == ImmSource {
  204. fmt.Fprintf(f, "imm: %d", ins.Constant)
  205. } else {
  206. fmt.Fprintf(f, "src: %s", ins.Src)
  207. }
  208. case JumpClass:
  209. switch jop := op.JumpOp(); jop {
  210. case Call:
  211. if ins.Src == PseudoCall {
  212. // bpf-to-bpf call
  213. fmt.Fprint(f, ins.Constant)
  214. } else {
  215. fmt.Fprint(f, BuiltinFunc(ins.Constant))
  216. }
  217. default:
  218. fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset)
  219. if op.Source() == ImmSource {
  220. fmt.Fprintf(f, "imm: %d", ins.Constant)
  221. } else {
  222. fmt.Fprintf(f, "src: %s", ins.Src)
  223. }
  224. }
  225. }
  226. ref:
  227. if ins.Reference != "" {
  228. fmt.Fprintf(f, " <%s>", ins.Reference)
  229. }
  230. }
  231. // Instructions is an eBPF program.
  232. type Instructions []Instruction
  233. func (insns Instructions) String() string {
  234. return fmt.Sprint(insns)
  235. }
  236. // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
  237. //
  238. // Returns an error if the symbol isn't used, see IsUnreferencedSymbol.
  239. func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
  240. if symbol == "" {
  241. return errors.New("empty symbol")
  242. }
  243. found := false
  244. for i := range insns {
  245. ins := &insns[i]
  246. if ins.Reference != symbol {
  247. continue
  248. }
  249. if err := ins.RewriteMapPtr(fd); err != nil {
  250. return err
  251. }
  252. found = true
  253. }
  254. if !found {
  255. return &unreferencedSymbolError{symbol}
  256. }
  257. return nil
  258. }
  259. // SymbolOffsets returns the set of symbols and their offset in
  260. // the instructions.
  261. func (insns Instructions) SymbolOffsets() (map[string]int, error) {
  262. offsets := make(map[string]int)
  263. for i, ins := range insns {
  264. if ins.Symbol == "" {
  265. continue
  266. }
  267. if _, ok := offsets[ins.Symbol]; ok {
  268. return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol)
  269. }
  270. offsets[ins.Symbol] = i
  271. }
  272. return offsets, nil
  273. }
  274. // ReferenceOffsets returns the set of references and their offset in
  275. // the instructions.
  276. func (insns Instructions) ReferenceOffsets() map[string][]int {
  277. offsets := make(map[string][]int)
  278. for i, ins := range insns {
  279. if ins.Reference == "" {
  280. continue
  281. }
  282. offsets[ins.Reference] = append(offsets[ins.Reference], i)
  283. }
  284. return offsets
  285. }
  286. // Format implements fmt.Formatter.
  287. //
  288. // You can control indentation of symbols by
  289. // specifying a width. Setting a precision controls the indentation of
  290. // instructions.
  291. // The default character is a tab, which can be overridden by specifying
  292. // the ' ' space flag.
  293. func (insns Instructions) Format(f fmt.State, c rune) {
  294. if c != 's' && c != 'v' {
  295. fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c)
  296. return
  297. }
  298. // Precision is better in this case, because it allows
  299. // specifying 0 padding easily.
  300. padding, ok := f.Precision()
  301. if !ok {
  302. padding = 1
  303. }
  304. indent := strings.Repeat("\t", padding)
  305. if f.Flag(' ') {
  306. indent = strings.Repeat(" ", padding)
  307. }
  308. symPadding, ok := f.Width()
  309. if !ok {
  310. symPadding = padding - 1
  311. }
  312. if symPadding < 0 {
  313. symPadding = 0
  314. }
  315. symIndent := strings.Repeat("\t", symPadding)
  316. if f.Flag(' ') {
  317. symIndent = strings.Repeat(" ", symPadding)
  318. }
  319. // Guess how many digits we need at most, by assuming that all instructions
  320. // are double wide.
  321. highestOffset := len(insns) * 2
  322. offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset))))
  323. iter := insns.Iterate()
  324. for iter.Next() {
  325. if iter.Ins.Symbol != "" {
  326. fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol)
  327. }
  328. fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins)
  329. }
  330. }
  331. // Marshal encodes a BPF program into the kernel format.
  332. func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
  333. for i, ins := range insns {
  334. _, err := ins.Marshal(w, bo)
  335. if err != nil {
  336. return fmt.Errorf("instruction %d: %w", i, err)
  337. }
  338. }
  339. return nil
  340. }
  341. // Tag calculates the kernel tag for a series of instructions.
  342. //
  343. // It mirrors bpf_prog_calc_tag in the kernel and so can be compared
  344. // to ProgramInfo.Tag to figure out whether a loaded program matches
  345. // certain instructions.
  346. func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
  347. h := sha1.New()
  348. for i, ins := range insns {
  349. if ins.IsLoadFromMap() {
  350. ins.Constant = 0
  351. }
  352. _, err := ins.Marshal(h, bo)
  353. if err != nil {
  354. return "", fmt.Errorf("instruction %d: %w", i, err)
  355. }
  356. }
  357. return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil
  358. }
  359. // Iterate allows iterating a BPF program while keeping track of
  360. // various offsets.
  361. //
  362. // Modifying the instruction slice will lead to undefined behaviour.
  363. func (insns Instructions) Iterate() *InstructionIterator {
  364. return &InstructionIterator{insns: insns}
  365. }
  366. // InstructionIterator iterates over a BPF program.
  367. type InstructionIterator struct {
  368. insns Instructions
  369. // The instruction in question.
  370. Ins *Instruction
  371. // The index of the instruction in the original instruction slice.
  372. Index int
  373. // The offset of the instruction in raw BPF instructions. This accounts
  374. // for double-wide instructions.
  375. Offset RawInstructionOffset
  376. }
  377. // Next returns true as long as there are any instructions remaining.
  378. func (iter *InstructionIterator) Next() bool {
  379. if len(iter.insns) == 0 {
  380. return false
  381. }
  382. if iter.Ins != nil {
  383. iter.Index++
  384. iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions())
  385. }
  386. iter.Ins = &iter.insns[0]
  387. iter.insns = iter.insns[1:]
  388. return true
  389. }
  390. type bpfInstruction struct {
  391. OpCode OpCode
  392. Registers bpfRegisters
  393. Offset int16
  394. Constant int32
  395. }
  396. type bpfRegisters uint8
  397. func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
  398. switch bo {
  399. case binary.LittleEndian:
  400. return bpfRegisters((src << 4) | (dst & 0xF)), nil
  401. case binary.BigEndian:
  402. return bpfRegisters((dst << 4) | (src & 0xF)), nil
  403. default:
  404. return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
  405. }
  406. }
  407. func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) {
  408. switch bo {
  409. case binary.LittleEndian:
  410. return Register(r & 0xF), Register(r >> 4), nil
  411. case binary.BigEndian:
  412. return Register(r >> 4), Register(r & 0xf), nil
  413. default:
  414. return 0, 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
  415. }
  416. }
  417. type unreferencedSymbolError struct {
  418. symbol string
  419. }
  420. func (use *unreferencedSymbolError) Error() string {
  421. return fmt.Sprintf("unreferenced symbol %s", use.symbol)
  422. }
  423. // IsUnreferencedSymbol returns true if err was caused by
  424. // an unreferenced symbol.
  425. func IsUnreferencedSymbol(err error) bool {
  426. _, ok := err.(*unreferencedSymbolError)
  427. return ok
  428. }