instruction.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. package asm
  2. import (
  3. "crypto/sha1"
  4. "encoding/binary"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "math"
  10. "sort"
  11. "strings"
  12. "github.com/cilium/ebpf/internal/sys"
  13. "github.com/cilium/ebpf/internal/unix"
  14. )
  15. // InstructionSize is the size of a BPF instruction in bytes
  16. const InstructionSize = 8
  17. // RawInstructionOffset is an offset in units of raw BPF instructions.
  18. type RawInstructionOffset uint64
  19. var ErrUnreferencedSymbol = errors.New("unreferenced symbol")
  20. var ErrUnsatisfiedMapReference = errors.New("unsatisfied map reference")
  21. var ErrUnsatisfiedProgramReference = errors.New("unsatisfied program reference")
  22. // Bytes returns the offset of an instruction in bytes.
  23. func (rio RawInstructionOffset) Bytes() uint64 {
  24. return uint64(rio) * InstructionSize
  25. }
  26. // Instruction is a single eBPF instruction.
  27. type Instruction struct {
  28. OpCode OpCode
  29. Dst Register
  30. Src Register
  31. Offset int16
  32. Constant int64
  33. // Metadata contains optional metadata about this instruction.
  34. Metadata Metadata
  35. }
  36. // Unmarshal decodes a BPF instruction.
  37. func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) {
  38. data := make([]byte, InstructionSize)
  39. if _, err := io.ReadFull(r, data); err != nil {
  40. return 0, err
  41. }
  42. ins.OpCode = OpCode(data[0])
  43. regs := data[1]
  44. switch bo {
  45. case binary.LittleEndian:
  46. ins.Dst, ins.Src = Register(regs&0xF), Register(regs>>4)
  47. case binary.BigEndian:
  48. ins.Dst, ins.Src = Register(regs>>4), Register(regs&0xf)
  49. }
  50. ins.Offset = int16(bo.Uint16(data[2:4]))
  51. // Convert to int32 before widening to int64
  52. // to ensure the signed bit is carried over.
  53. ins.Constant = int64(int32(bo.Uint32(data[4:8])))
  54. if !ins.OpCode.IsDWordLoad() {
  55. return InstructionSize, nil
  56. }
  57. // Pull another instruction from the stream to retrieve the second
  58. // half of the 64-bit immediate value.
  59. if _, err := io.ReadFull(r, data); err != nil {
  60. // No Wrap, to avoid io.EOF clash
  61. return 0, errors.New("64bit immediate is missing second half")
  62. }
  63. // Require that all fields other than the value are zero.
  64. if bo.Uint32(data[0:4]) != 0 {
  65. return 0, errors.New("64bit immediate has non-zero fields")
  66. }
  67. cons1 := uint32(ins.Constant)
  68. cons2 := int32(bo.Uint32(data[4:8]))
  69. ins.Constant = int64(cons2)<<32 | int64(cons1)
  70. return 2 * InstructionSize, nil
  71. }
  72. // Marshal encodes a BPF instruction.
  73. func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {
  74. if ins.OpCode == InvalidOpCode {
  75. return 0, errors.New("invalid opcode")
  76. }
  77. isDWordLoad := ins.OpCode.IsDWordLoad()
  78. cons := int32(ins.Constant)
  79. if isDWordLoad {
  80. // Encode least significant 32bit first for 64bit operations.
  81. cons = int32(uint32(ins.Constant))
  82. }
  83. regs, err := newBPFRegisters(ins.Dst, ins.Src, bo)
  84. if err != nil {
  85. return 0, fmt.Errorf("can't marshal registers: %s", err)
  86. }
  87. data := make([]byte, InstructionSize)
  88. data[0] = byte(ins.OpCode)
  89. data[1] = byte(regs)
  90. bo.PutUint16(data[2:4], uint16(ins.Offset))
  91. bo.PutUint32(data[4:8], uint32(cons))
  92. if _, err := w.Write(data); err != nil {
  93. return 0, err
  94. }
  95. if !isDWordLoad {
  96. return InstructionSize, nil
  97. }
  98. // The first half of the second part of a double-wide instruction
  99. // must be zero. The second half carries the value.
  100. bo.PutUint32(data[0:4], 0)
  101. bo.PutUint32(data[4:8], uint32(ins.Constant>>32))
  102. if _, err := w.Write(data); err != nil {
  103. return 0, err
  104. }
  105. return 2 * InstructionSize, nil
  106. }
  107. // AssociateMap associates a Map with this Instruction.
  108. //
  109. // Implicitly clears the Instruction's Reference field.
  110. //
  111. // Returns an error if the Instruction is not a map load.
  112. func (ins *Instruction) AssociateMap(m FDer) error {
  113. if !ins.IsLoadFromMap() {
  114. return errors.New("not a load from a map")
  115. }
  116. ins.Metadata.Set(referenceMeta{}, nil)
  117. ins.Metadata.Set(mapMeta{}, m)
  118. return nil
  119. }
  120. // RewriteMapPtr changes an instruction to use a new map fd.
  121. //
  122. // Returns an error if the instruction doesn't load a map.
  123. //
  124. // Deprecated: use AssociateMap instead. If you cannot provide a Map,
  125. // wrap an fd in a type implementing FDer.
  126. func (ins *Instruction) RewriteMapPtr(fd int) error {
  127. if !ins.IsLoadFromMap() {
  128. return errors.New("not a load from a map")
  129. }
  130. ins.encodeMapFD(fd)
  131. return nil
  132. }
  133. func (ins *Instruction) encodeMapFD(fd int) {
  134. // Preserve the offset value for direct map loads.
  135. offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
  136. rawFd := uint64(uint32(fd))
  137. ins.Constant = int64(offset | rawFd)
  138. }
  139. // MapPtr returns the map fd for this instruction.
  140. //
  141. // The result is undefined if the instruction is not a load from a map,
  142. // see IsLoadFromMap.
  143. //
  144. // Deprecated: use Map() instead.
  145. func (ins *Instruction) MapPtr() int {
  146. // If there is a map associated with the instruction, return its FD.
  147. if fd := ins.Metadata.Get(mapMeta{}); fd != nil {
  148. return fd.(FDer).FD()
  149. }
  150. // Fall back to the fd stored in the Constant field
  151. return ins.mapFd()
  152. }
  153. // mapFd returns the map file descriptor stored in the 32 least significant
  154. // bits of ins' Constant field.
  155. func (ins *Instruction) mapFd() int {
  156. return int(int32(ins.Constant))
  157. }
  158. // RewriteMapOffset changes the offset of a direct load from a map.
  159. //
  160. // Returns an error if the instruction is not a direct load.
  161. func (ins *Instruction) RewriteMapOffset(offset uint32) error {
  162. if !ins.OpCode.IsDWordLoad() {
  163. return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
  164. }
  165. if ins.Src != PseudoMapValue {
  166. return errors.New("not a direct load from a map")
  167. }
  168. fd := uint64(ins.Constant) & math.MaxUint32
  169. ins.Constant = int64(uint64(offset)<<32 | fd)
  170. return nil
  171. }
  172. func (ins *Instruction) mapOffset() uint32 {
  173. return uint32(uint64(ins.Constant) >> 32)
  174. }
  175. // IsLoadFromMap returns true if the instruction loads from a map.
  176. //
  177. // This covers both loading the map pointer and direct map value loads.
  178. func (ins *Instruction) IsLoadFromMap() bool {
  179. return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
  180. }
  181. // IsFunctionCall returns true if the instruction calls another BPF function.
  182. //
  183. // This is not the same thing as a BPF helper call.
  184. func (ins *Instruction) IsFunctionCall() bool {
  185. return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
  186. }
  187. // IsKfuncCall returns true if the instruction calls a kfunc.
  188. //
  189. // This is not the same thing as a BPF helper call.
  190. func (ins *Instruction) IsKfuncCall() bool {
  191. return ins.OpCode.JumpOp() == Call && ins.Src == PseudoKfuncCall
  192. }
  193. // IsLoadOfFunctionPointer returns true if the instruction loads a function pointer.
  194. func (ins *Instruction) IsLoadOfFunctionPointer() bool {
  195. return ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc
  196. }
  197. // IsFunctionReference returns true if the instruction references another BPF
  198. // function, either by invoking a Call jump operation or by loading a function
  199. // pointer.
  200. func (ins *Instruction) IsFunctionReference() bool {
  201. return ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer()
  202. }
  203. // IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call.
  204. func (ins *Instruction) IsBuiltinCall() bool {
  205. return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0
  206. }
  207. // IsConstantLoad returns true if the instruction loads a constant of the
  208. // given size.
  209. func (ins *Instruction) IsConstantLoad(size Size) bool {
  210. return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0
  211. }
  212. // Format implements fmt.Formatter.
  213. func (ins Instruction) Format(f fmt.State, c rune) {
  214. if c != 'v' {
  215. fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c)
  216. return
  217. }
  218. op := ins.OpCode
  219. if op == InvalidOpCode {
  220. fmt.Fprint(f, "INVALID")
  221. return
  222. }
  223. // Omit trailing space for Exit
  224. if op.JumpOp() == Exit {
  225. fmt.Fprint(f, op)
  226. return
  227. }
  228. if ins.IsLoadFromMap() {
  229. fd := ins.mapFd()
  230. m := ins.Map()
  231. switch ins.Src {
  232. case PseudoMapFD:
  233. if m != nil {
  234. fmt.Fprintf(f, "LoadMapPtr dst: %s map: %s", ins.Dst, m)
  235. } else {
  236. fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd)
  237. }
  238. case PseudoMapValue:
  239. if m != nil {
  240. fmt.Fprintf(f, "LoadMapValue dst: %s, map: %s off: %d", ins.Dst, m, ins.mapOffset())
  241. } else {
  242. fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset())
  243. }
  244. }
  245. goto ref
  246. }
  247. fmt.Fprintf(f, "%v ", op)
  248. switch cls := op.Class(); {
  249. case cls.isLoadOrStore():
  250. switch op.Mode() {
  251. case ImmMode:
  252. fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant)
  253. case AbsMode:
  254. fmt.Fprintf(f, "imm: %d", ins.Constant)
  255. case IndMode:
  256. fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant)
  257. case MemMode:
  258. fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant)
  259. case XAddMode:
  260. fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src)
  261. }
  262. case cls.IsALU():
  263. fmt.Fprintf(f, "dst: %s ", ins.Dst)
  264. if op.ALUOp() == Swap || op.Source() == ImmSource {
  265. fmt.Fprintf(f, "imm: %d", ins.Constant)
  266. } else {
  267. fmt.Fprintf(f, "src: %s", ins.Src)
  268. }
  269. case cls.IsJump():
  270. switch jop := op.JumpOp(); jop {
  271. case Call:
  272. switch ins.Src {
  273. case PseudoCall:
  274. // bpf-to-bpf call
  275. fmt.Fprint(f, ins.Constant)
  276. case PseudoKfuncCall:
  277. // kfunc call
  278. fmt.Fprintf(f, "Kfunc(%d)", ins.Constant)
  279. default:
  280. fmt.Fprint(f, BuiltinFunc(ins.Constant))
  281. }
  282. default:
  283. fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset)
  284. if op.Source() == ImmSource {
  285. fmt.Fprintf(f, "imm: %d", ins.Constant)
  286. } else {
  287. fmt.Fprintf(f, "src: %s", ins.Src)
  288. }
  289. }
  290. }
  291. ref:
  292. if ins.Reference() != "" {
  293. fmt.Fprintf(f, " <%s>", ins.Reference())
  294. }
  295. }
  296. func (ins Instruction) equal(other Instruction) bool {
  297. return ins.OpCode == other.OpCode &&
  298. ins.Dst == other.Dst &&
  299. ins.Src == other.Src &&
  300. ins.Offset == other.Offset &&
  301. ins.Constant == other.Constant
  302. }
  303. // Size returns the amount of bytes ins would occupy in binary form.
  304. func (ins Instruction) Size() uint64 {
  305. return uint64(InstructionSize * ins.OpCode.rawInstructions())
  306. }
  307. // WithMetadata sets the given Metadata on the Instruction. e.g. to copy
  308. // Metadata from another Instruction when replacing it.
  309. func (ins Instruction) WithMetadata(meta Metadata) Instruction {
  310. ins.Metadata = meta
  311. return ins
  312. }
  313. type symbolMeta struct{}
  314. // WithSymbol marks the Instruction as a Symbol, which other Instructions
  315. // can point to using corresponding calls to WithReference.
  316. func (ins Instruction) WithSymbol(name string) Instruction {
  317. ins.Metadata.Set(symbolMeta{}, name)
  318. return ins
  319. }
  320. // Sym creates a symbol.
  321. //
  322. // Deprecated: use WithSymbol instead.
  323. func (ins Instruction) Sym(name string) Instruction {
  324. return ins.WithSymbol(name)
  325. }
  326. // Symbol returns the value ins has been marked with using WithSymbol,
  327. // otherwise returns an empty string. A symbol is often an Instruction
  328. // at the start of a function body.
  329. func (ins Instruction) Symbol() string {
  330. sym, _ := ins.Metadata.Get(symbolMeta{}).(string)
  331. return sym
  332. }
  333. type referenceMeta struct{}
  334. // WithReference makes ins reference another Symbol or map by name.
  335. func (ins Instruction) WithReference(ref string) Instruction {
  336. ins.Metadata.Set(referenceMeta{}, ref)
  337. return ins
  338. }
  339. // Reference returns the Symbol or map name referenced by ins, if any.
  340. func (ins Instruction) Reference() string {
  341. ref, _ := ins.Metadata.Get(referenceMeta{}).(string)
  342. return ref
  343. }
  344. type mapMeta struct{}
  345. // Map returns the Map referenced by ins, if any.
  346. // An Instruction will contain a Map if e.g. it references an existing,
  347. // pinned map that was opened during ELF loading.
  348. func (ins Instruction) Map() FDer {
  349. fd, _ := ins.Metadata.Get(mapMeta{}).(FDer)
  350. return fd
  351. }
  352. type sourceMeta struct{}
  353. // WithSource adds source information about the Instruction.
  354. func (ins Instruction) WithSource(src fmt.Stringer) Instruction {
  355. ins.Metadata.Set(sourceMeta{}, src)
  356. return ins
  357. }
  358. // Source returns source information about the Instruction. The field is
  359. // present when the compiler emits BTF line info about the Instruction and
  360. // usually contains the line of source code responsible for it.
  361. func (ins Instruction) Source() fmt.Stringer {
  362. str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer)
  363. return str
  364. }
  365. // A Comment can be passed to Instruction.WithSource to add a comment
  366. // to an instruction.
  367. type Comment string
  368. func (s Comment) String() string {
  369. return string(s)
  370. }
  371. // FDer represents a resource tied to an underlying file descriptor.
  372. // Used as a stand-in for e.g. ebpf.Map since that type cannot be
  373. // imported here and FD() is the only method we rely on.
  374. type FDer interface {
  375. FD() int
  376. }
  377. // Instructions is an eBPF program.
  378. type Instructions []Instruction
  379. // Unmarshal unmarshals an Instructions from a binary instruction stream.
  380. // All instructions in insns are replaced by instructions decoded from r.
  381. func (insns *Instructions) Unmarshal(r io.Reader, bo binary.ByteOrder) error {
  382. if len(*insns) > 0 {
  383. *insns = nil
  384. }
  385. var offset uint64
  386. for {
  387. var ins Instruction
  388. n, err := ins.Unmarshal(r, bo)
  389. if errors.Is(err, io.EOF) {
  390. break
  391. }
  392. if err != nil {
  393. return fmt.Errorf("offset %d: %w", offset, err)
  394. }
  395. *insns = append(*insns, ins)
  396. offset += n
  397. }
  398. return nil
  399. }
  400. // Name returns the name of the function insns belongs to, if any.
  401. func (insns Instructions) Name() string {
  402. if len(insns) == 0 {
  403. return ""
  404. }
  405. return insns[0].Symbol()
  406. }
  407. func (insns Instructions) String() string {
  408. return fmt.Sprint(insns)
  409. }
  410. // Size returns the amount of bytes insns would occupy in binary form.
  411. func (insns Instructions) Size() uint64 {
  412. var sum uint64
  413. for _, ins := range insns {
  414. sum += ins.Size()
  415. }
  416. return sum
  417. }
  418. // AssociateMap updates all Instructions that Reference the given symbol
  419. // to point to an existing Map m instead.
  420. //
  421. // Returns ErrUnreferencedSymbol error if no references to symbol are found
  422. // in insns. If symbol is anything else than the symbol name of map (e.g.
  423. // a bpf2bpf subprogram), an error is returned.
  424. func (insns Instructions) AssociateMap(symbol string, m FDer) error {
  425. if symbol == "" {
  426. return errors.New("empty symbol")
  427. }
  428. var found bool
  429. for i := range insns {
  430. ins := &insns[i]
  431. if ins.Reference() != symbol {
  432. continue
  433. }
  434. if err := ins.AssociateMap(m); err != nil {
  435. return err
  436. }
  437. found = true
  438. }
  439. if !found {
  440. return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
  441. }
  442. return nil
  443. }
  444. // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
  445. //
  446. // Returns ErrUnreferencedSymbol if the symbol isn't used.
  447. //
  448. // Deprecated: use AssociateMap instead.
  449. func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
  450. if symbol == "" {
  451. return errors.New("empty symbol")
  452. }
  453. var found bool
  454. for i := range insns {
  455. ins := &insns[i]
  456. if ins.Reference() != symbol {
  457. continue
  458. }
  459. if !ins.IsLoadFromMap() {
  460. return errors.New("not a load from a map")
  461. }
  462. ins.encodeMapFD(fd)
  463. found = true
  464. }
  465. if !found {
  466. return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
  467. }
  468. return nil
  469. }
  470. // SymbolOffsets returns the set of symbols and their offset in
  471. // the instructions.
  472. func (insns Instructions) SymbolOffsets() (map[string]int, error) {
  473. offsets := make(map[string]int)
  474. for i, ins := range insns {
  475. if ins.Symbol() == "" {
  476. continue
  477. }
  478. if _, ok := offsets[ins.Symbol()]; ok {
  479. return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol())
  480. }
  481. offsets[ins.Symbol()] = i
  482. }
  483. return offsets, nil
  484. }
  485. // FunctionReferences returns a set of symbol names these Instructions make
  486. // bpf-to-bpf calls to.
  487. func (insns Instructions) FunctionReferences() []string {
  488. calls := make(map[string]struct{})
  489. for _, ins := range insns {
  490. if ins.Constant != -1 {
  491. // BPF-to-BPF calls have -1 constants.
  492. continue
  493. }
  494. if ins.Reference() == "" {
  495. continue
  496. }
  497. if !ins.IsFunctionReference() {
  498. continue
  499. }
  500. calls[ins.Reference()] = struct{}{}
  501. }
  502. result := make([]string, 0, len(calls))
  503. for call := range calls {
  504. result = append(result, call)
  505. }
  506. sort.Strings(result)
  507. return result
  508. }
  509. // ReferenceOffsets returns the set of references and their offset in
  510. // the instructions.
  511. func (insns Instructions) ReferenceOffsets() map[string][]int {
  512. offsets := make(map[string][]int)
  513. for i, ins := range insns {
  514. if ins.Reference() == "" {
  515. continue
  516. }
  517. offsets[ins.Reference()] = append(offsets[ins.Reference()], i)
  518. }
  519. return offsets
  520. }
  521. // Format implements fmt.Formatter.
  522. //
  523. // You can control indentation of symbols by
  524. // specifying a width. Setting a precision controls the indentation of
  525. // instructions.
  526. // The default character is a tab, which can be overridden by specifying
  527. // the ' ' space flag.
  528. func (insns Instructions) Format(f fmt.State, c rune) {
  529. if c != 's' && c != 'v' {
  530. fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c)
  531. return
  532. }
  533. // Precision is better in this case, because it allows
  534. // specifying 0 padding easily.
  535. padding, ok := f.Precision()
  536. if !ok {
  537. padding = 1
  538. }
  539. indent := strings.Repeat("\t", padding)
  540. if f.Flag(' ') {
  541. indent = strings.Repeat(" ", padding)
  542. }
  543. symPadding, ok := f.Width()
  544. if !ok {
  545. symPadding = padding - 1
  546. }
  547. if symPadding < 0 {
  548. symPadding = 0
  549. }
  550. symIndent := strings.Repeat("\t", symPadding)
  551. if f.Flag(' ') {
  552. symIndent = strings.Repeat(" ", symPadding)
  553. }
  554. // Guess how many digits we need at most, by assuming that all instructions
  555. // are double wide.
  556. highestOffset := len(insns) * 2
  557. offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset))))
  558. iter := insns.Iterate()
  559. for iter.Next() {
  560. if iter.Ins.Symbol() != "" {
  561. fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol())
  562. }
  563. if src := iter.Ins.Source(); src != nil {
  564. line := strings.TrimSpace(src.String())
  565. if line != "" {
  566. fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line)
  567. }
  568. }
  569. fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins)
  570. }
  571. }
  572. // Marshal encodes a BPF program into the kernel format.
  573. //
  574. // insns may be modified if there are unresolved jumps or bpf2bpf calls.
  575. //
  576. // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
  577. // without a matching Symbol Instruction within insns.
  578. func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
  579. if err := insns.encodeFunctionReferences(); err != nil {
  580. return err
  581. }
  582. if err := insns.encodeMapPointers(); err != nil {
  583. return err
  584. }
  585. for i, ins := range insns {
  586. if _, err := ins.Marshal(w, bo); err != nil {
  587. return fmt.Errorf("instruction %d: %w", i, err)
  588. }
  589. }
  590. return nil
  591. }
  592. // Tag calculates the kernel tag for a series of instructions.
  593. //
  594. // It mirrors bpf_prog_calc_tag in the kernel and so can be compared
  595. // to ProgramInfo.Tag to figure out whether a loaded program matches
  596. // certain instructions.
  597. func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
  598. h := sha1.New()
  599. for i, ins := range insns {
  600. if ins.IsLoadFromMap() {
  601. ins.Constant = 0
  602. }
  603. _, err := ins.Marshal(h, bo)
  604. if err != nil {
  605. return "", fmt.Errorf("instruction %d: %w", i, err)
  606. }
  607. }
  608. return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil
  609. }
  610. // encodeFunctionReferences populates the Offset (or Constant, depending on
  611. // the instruction type) field of instructions with a Reference field to point
  612. // to the offset of the corresponding instruction with a matching Symbol field.
  613. //
  614. // Only Reference Instructions that are either jumps or BPF function references
  615. // (calls or function pointer loads) are populated.
  616. //
  617. // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
  618. // without at least one corresponding Symbol Instruction within insns.
  619. func (insns Instructions) encodeFunctionReferences() error {
  620. // Index the offsets of instructions tagged as a symbol.
  621. symbolOffsets := make(map[string]RawInstructionOffset)
  622. iter := insns.Iterate()
  623. for iter.Next() {
  624. ins := iter.Ins
  625. if ins.Symbol() == "" {
  626. continue
  627. }
  628. if _, ok := symbolOffsets[ins.Symbol()]; ok {
  629. return fmt.Errorf("duplicate symbol %s", ins.Symbol())
  630. }
  631. symbolOffsets[ins.Symbol()] = iter.Offset
  632. }
  633. // Find all instructions tagged as references to other symbols.
  634. // Depending on the instruction type, populate their constant or offset
  635. // fields to point to the symbol they refer to within the insn stream.
  636. iter = insns.Iterate()
  637. for iter.Next() {
  638. i := iter.Index
  639. offset := iter.Offset
  640. ins := iter.Ins
  641. if ins.Reference() == "" {
  642. continue
  643. }
  644. switch {
  645. case ins.IsFunctionReference() && ins.Constant == -1:
  646. symOffset, ok := symbolOffsets[ins.Reference()]
  647. if !ok {
  648. return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
  649. }
  650. ins.Constant = int64(symOffset - offset - 1)
  651. case ins.OpCode.Class().IsJump() && ins.Offset == -1:
  652. symOffset, ok := symbolOffsets[ins.Reference()]
  653. if !ok {
  654. return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
  655. }
  656. ins.Offset = int16(symOffset - offset - 1)
  657. }
  658. }
  659. return nil
  660. }
  661. // encodeMapPointers finds all Map Instructions and encodes their FDs
  662. // into their Constant fields.
  663. func (insns Instructions) encodeMapPointers() error {
  664. iter := insns.Iterate()
  665. for iter.Next() {
  666. ins := iter.Ins
  667. if !ins.IsLoadFromMap() {
  668. continue
  669. }
  670. m := ins.Map()
  671. if m == nil {
  672. continue
  673. }
  674. fd := m.FD()
  675. if fd < 0 {
  676. return fmt.Errorf("map %s: %w", m, sys.ErrClosedFd)
  677. }
  678. ins.encodeMapFD(m.FD())
  679. }
  680. return nil
  681. }
  682. // Iterate allows iterating a BPF program while keeping track of
  683. // various offsets.
  684. //
  685. // Modifying the instruction slice will lead to undefined behaviour.
  686. func (insns Instructions) Iterate() *InstructionIterator {
  687. return &InstructionIterator{insns: insns}
  688. }
  689. // InstructionIterator iterates over a BPF program.
  690. type InstructionIterator struct {
  691. insns Instructions
  692. // The instruction in question.
  693. Ins *Instruction
  694. // The index of the instruction in the original instruction slice.
  695. Index int
  696. // The offset of the instruction in raw BPF instructions. This accounts
  697. // for double-wide instructions.
  698. Offset RawInstructionOffset
  699. }
  700. // Next returns true as long as there are any instructions remaining.
  701. func (iter *InstructionIterator) Next() bool {
  702. if len(iter.insns) == 0 {
  703. return false
  704. }
  705. if iter.Ins != nil {
  706. iter.Index++
  707. iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions())
  708. }
  709. iter.Ins = &iter.insns[0]
  710. iter.insns = iter.insns[1:]
  711. return true
  712. }
  713. type bpfRegisters uint8
  714. func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
  715. switch bo {
  716. case binary.LittleEndian:
  717. return bpfRegisters((src << 4) | (dst & 0xF)), nil
  718. case binary.BigEndian:
  719. return bpfRegisters((dst << 4) | (src & 0xF)), nil
  720. default:
  721. return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
  722. }
  723. }
  724. // IsUnreferencedSymbol returns true if err was caused by
  725. // an unreferenced symbol.
  726. //
  727. // Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol).
  728. func IsUnreferencedSymbol(err error) bool {
  729. return errors.Is(err, ErrUnreferencedSymbol)
  730. }