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 
1
  • 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. Commented May 18, 2018 at 3:31

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

6 Comments

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.
@IgorLiferenko: I'm not sure if it's possible to use external debug symbols with objdump -d -S.
@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".
@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 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.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.