0

Consider the following part of output from dump of a binary file (amd64):

$ objdump -D /lib/modules/4.16.0-1-amd64/kernel/drivers/usb/class/cdc-acm.ko
...
25f0:       e8 00 00 00 00          callq  25f5 <acm_port_dtr_rts+0x5>
25f5:       53                      push   %rbx
25f6:       48 89 fb                mov    %rdi,%rbx
25f9:       48 83 ef 20             sub    $0x20,%rdi
25fd:       85 f6                   test   %esi,%esi
25ff:       74 3c                   je     263d <acm_port_dtr_rts+0x4d>
2601:       b8 03 00 00 00          mov    $0x3,%eax
2606:       b9 03 00 00 00          mov    $0x3,%ecx
260b:       f6 83 60 08 00 00 40    testb  $0x40,0x860(%rbx)
2612:       89 83 94 07 00 00       mov    %eax,0x794(%rbx)
2618:       75 18                   jne    2632 <acm_port_dtr_rts+0x42>
261a:       48 8d 73 e8             lea    -0x18(%rbx),%rsi
261e:       45 31 c9                xor    %r9d,%r9d
2621:       45 31 c0                xor    %r8d,%r8d
2624:       ba 22 00 00 00          mov    $0x22,%edx
2629:       e8 32 eb ff ff          callq  1160 <acm_ctrl_msg.isra.10>
262e:       85 c0                   test   %eax,%eax
2630:       74 09                   je     263b <acm_port_dtr_rts+0x4b>
2632:       f6 83 1c 08 00 00 02    testb  $0x2,0x81c(%rbx)
2639:       75 08                   jne    2643 <acm_port_dtr_rts+0x53>
263b:       5b                      pop    %rbx
263c:       c3                      retq
263d:       89 f1                   mov    %esi,%ecx
263f:       31 c0                   xor    %eax,%eax
2641:       eb c8                   jmp    260b <acm_port_dtr_rts+0x1b>
2643:       48 8b 7b e8             mov    -0x18(%rbx),%rdi
2647:       48 c7 c6 00 00 00 00    mov    $0x0,%rsi
264e:       5b                      pop    %rbx
264f:       48 83 c7 30             add    $0x30,%rdi
2653:       e9 00 00 00 00          jmpq   2658 <acm_port_dtr_rts+0x68>
2658:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
265f:       00
...

The following part from cdc-acm.c corresponds to above dump:

static void acm_port_dtr_rts(struct tty_port *port, int raise)
{
  struct acm *acm = container_of(port, struct acm, port);
  int val;
  int res;

  if (raise)
        val = ACM_CTRL_DTR | ACM_CTRL_RTS;
  else
        val = 0;

  acm->ctrlout = val;

  res = acm_set_control(acm, val);
  if (res && (acm->ctrl_caps & USB_CDC_CAP_LINE))
        dev_err(&acm->control->dev, "failed to set dtr/rts\n");
}

Also, the following constants are used here:

#define ACM_CTRL_DTR            0x01
#define ACM_CTRL_RTS            0x02

How to find out which line in the dump corresponds to the following line from source code?

val = ACM_CTRL_DTR | ACM_CTRL_RTS;

EDIT

The following procedure was used to change the binary:

hexdump -v -e "1/1 \" %02x\n\"" cdc-acm.ko.orig >text_file
<edit text_file>
xxd -r -p text_file cdc-acm.ko
Igor Liferenko
  • 1,499
  • 1
  • 13
  • 28
  • In csc-acm.h `ACM_CTRL_DTR` is 0x01 and `ACM_CTRL_RTS` is 0x02 . 0x01 | 0x02 = 0x03. The compiler computed this as a compile time optimization. – Michael Petch May 18 '18 at 03:31

1 Answers1

3

If you compiled with debug info, then objdump -d -S to interleave source lines with asm. gdb can use external debug symbols (like from linux-image-4.16.0-1-amd64-dbg) but I don't think that's useful for disassembling kernel modules.

I'm not sure how to tell objdump to look for / use them. See https://www.technovelty.org/code/separate-debug-info.html for some more info about separate debug info, but it doesn't say anything about objdump -S using them, only gdb.


Otherwise, ACM_CTRL_DTR | ACM_CTRL_RTS is 0x3, and there's a test/je that skips over a couple mov $3, %eax / mov $3, %ecx instructions when something %esi is zero, so that's if(raise) branch. The 2nd integer function arg is passed in RSI/ESI in the x86-64 System V calling convention, so that's raise unless it's been clobbered by an earlier call or some other thing you hid with ....

The two mov instructions are probably explained by assigning val to something else, but I haven't tried to fully follow the logic.

Community
  • 1
  • 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • `raise` comes from function argument - I have added it to OP. Also, there is the following debug package in Debian `linux-image-4.16.0-1-amd64-dbg`. Is it possible to use it, and if yes - how? The final goal is to change that value to `0` without recompiling the whole kernel. – Igor Liferenko May 18 '18 at 03:47
  • @IgorLiferenko: I'm not sure if it's possible to use external debug symbols with `objdump -d -S`. – Peter Cordes May 18 '18 at 03:55
  • 1
    @IgorLiferenko looks to me like changing `25fd: 85 f6 test %esi,%esi` to `31 f6 xorl %esi,%esi` will achieve what you want, but modifying machine code after compilation from C source is always fragile, because next time after some subtle source change (even in different part of that source file, or some header which is included) the compiler may produce considerably different machine code, so maybe you reconsider how to adjust it in the source before build (or how to build quickly). Or at least detect wider area of opcodes around, so you will patch it only when the code is "like this". – Ped7g May 18 '18 at 06:47
  • @IgorLiferenko that proposed patch is like changing `if (raise)` to `if (raise=0)` (that's not a typo, it's assignment inside the `if` expression). – Ped7g May 18 '18 at 06:49
  • @Ped7g I tried to simply change `03` to `00` in two places, and it worked perfectly. See **EDIT** in OP. I did not try your variant yet. Do you think my solution is good, or yours would be preferable? P.S. I use local machine for testing - I can recompile kernel in Debian without problems - the main usage of this hack is necessary on openwrt devices (which I update very rarely) which I have quite a few, and I use image builder to generate firmware for them (compile from source would be too much hassle). So, binary patch is a good solution. – Igor Liferenko May 18 '18 at 07:32
  • @IgorLiferenko changing both `3` to `0` seems also valid approach. `raise` doesn't seem to be used in following code, so the both variants are identical, my would be somewhat different if `raise` would be used later again, yours modifies only `val`. – Ped7g May 18 '18 at 07:38