I use some third-party libraries in a project.
This project is a embedded system with a MIPS(isa32r2) core.
Recently we found a bug in the third-party library(libusb.a), but because of the time there is no technical support anymore.
so I try to disassemble the libarary.
$ mips-sde-elf-ar -x libusb.a
$ mips-sde-elf-objdump.exe -Ds -mmips:isa32r2 -EL usbhost_func.o > usbhost_func.s
in file usbhost_func.s I can get some info like:
Disassembly of section .text.usbhost_init:
00000000 :
0: 27bdffd8 addiu sp,sp,-40
4: 3c020000 lui v0,0x0
.text.usbhost_init looks like a complete disassembly functions of usbhost_init().
but there is some other info here like:
Disassembly of section .pdr:
00000000 :
0: 00000000 nop
4: 40000000 mfc0 zero,c0_index
8: fffffffc sdc3 $31,-4(ra)
... why here is ...
14: 00000008 jr zero
18: 0000001e 0x1e
1c: 0000001f 0x1f
20: 00000000 nop
Disassembly of section .gnu.attributes:
00000000 :
0: 00000f41 0xf41
4: 756e6700 jalx 5b99c00
8: 00070100 sll zero,a3,0x4
c: 03040000 0x3040000
so my question is:
What does the secton's mean like section .pdr?
Why there is some ... in setion .pdr ?
What is the srart point to disassemble a mips library?
Any hint and info are welcome.Tks.
objdump -D will try to deassemble all sections instead of sections which include valid instructions, you should use objdump -d.
.pdr is a debug information section, may not include valid instructions.
For static libraries (.a files), you should extract the object files in these libraries by ar x libfoo.a, and try to deassemble those object files you got by objdump -d.
Related
I build a 32-bit RISC-V CPU with Harvard architecture and I want to write programs for it in C. I have a RISC-V compiler set (https://xpack.github.io/riscv-none-embed-gcc/) that can do just that and works fine - for most things. The problem starts when I want to work with global variables, global arrays, etc, because these types get copied to RAM on boot/reset by the start script.
Here is a block diagram of my CPU: (This will be important later. Just note that the Instruction memory = FLASH and Data memory = RAM)
(If you are interested about my CPU, I made a video about it: https://www.youtube.com/watch?v=KzSaFFpBPDM)
Example:
A typical program will look something like this:
#include <stdint.h>
int static_var_1 = 2;
int static_var_2 = 4;
int main(void)
{
int var = static_var_1 + static_var_2;
}
And its objdump something like this:
/opt/xpack-riscv-none-embed-gcc-10.1.0-1.1/riscv-none-embed/bin/objdump build/APP.elf -D
build/APP.elf: file format elf32-littleriscv
Disassembly of section .text:
00000000 <_start>:
0: 00080137 lui sp,0x80
4: ffc10113 addi sp,sp,-4 # 7fffc <_estack>
8: 00c000ef jal ra,14 <main>
c: 0040006f j 10 <_exit>
00000010 <_exit>:
10: 0000006f j 10 <_exit>
00000014 <main>:
14: fe010113 addi sp,sp,-32
18: 00812e23 sw s0,28(sp)
1c: 02010413 addi s0,sp,32
20: 00002703 lw a4,0(zero) # 0 <_start>
24: 00402783 lw a5,4(zero) # 4 <static_var_2>
28: 00f707b3 add a5,a4,a5
2c: fef42623 sw a5,-20(s0)
30: 00000793 li a5,0
34: 00078513 mv a0,a5
38: 01c12403 lw s0,28(sp)
3c: 02010113 addi sp,sp,32
40: 00008067 ret
Disassembly of section .data:
00000000 <static_var_1>:
0: 0002 c.slli64 zero
...
00000004 <static_var_2>:
4: 0004 0x4
...
Disassembly of section ._user_heap_stack:
00000008 <._user_heap_stack>:
...
(the <_start> is a part of my start script that will initialize stack pointer)
The catch:
These are the two instructions that tries to load the global variables:
20: 00002703 lw a4,0(zero) # 0 <_start>
24: 00402783 lw a5,4(zero) # 4 <static_var_2>
But there is a problem - they were never put into RAM, so the CPU will most likely end up with some garbage data, which is unacceptable.
The solution?
Somebody suggested linker relaxation as part of my previous question (RISC-V: Global variables), again, that doesn't seem to be the case, but I can still be wrong though!
From my research, most of the "classic" CPUs use a start script, where the copying takes place, but as this is not a von-neuman architecture, I don't have the FLASH memory mapped to data memory and therefor cannot be read by the program (see the block diagram above). The output program must contain the variables already decoded as executable instructions, for example if we want value 0x4 in RAM at position 0x0, It can be decoded to:
addi t0, zero, 0x4
sw t0, 0(zero)
Re-building my CPU as von-neuman would require much more gates and ICs and this is a discrete build where every IC counts.
Doing it by hardware is for me the worst solution as I stated above, so if it can be done in software I'm all for it - and It can! Obviously, there is a solution, but by far the ugliest: Compile the code, extract the data (with python), generate a new startup script with these variables decoded by the python script and compile it again.
I really don't want to go that route, so if it can be done by modifying startup script, linker, etc, it would be really, really great.
AVR ICs are basically Harvard architecture (though modified) so do they something differently that we can learn from?
I am in the situation to have a c static library (compiled with arm-gcc), which is provided by a third party. I have no possibility to (let the third party) re-compile the library.
When investigating the library contents, i found that the gcc options -ffunction-sections and -fdata-sections have not been used for compiling the library. But this would be very helpful for reducing the binary size of the project.
Compilation is done with: (GNU Tools for ARM Embedded Processors) 4.8.4 20140526 (release) [ARM/embedded-4_8-branch revision 211358].
Is there any way to put every data and every function into their own separate section to enable function-level-linking for this library, without needing to recompile code?
I thought of this possible approach:
Split library into its object files.
For each object file:
Write code to move the symbols into own sections
Put new object files back together into archive file
Could this work, or do you have other suggestions, which ideally only use the tools provided by arm-gcc?
I'm aware this is old, but I came across this problem as well, and figured I'd provide my findings.
TL;DR: It's possible, but incredibly difficult. You can't simply move symbols into their own sections. Relocations will bite you.
When the compiler generates machine code, it will generate slightly different instructions if the -ffunction-sections and -fdata-sections flags are, or are not, provided. This is due to assumptions the compiler is able to make about where symbols will be located. These assumptions change depending on the flags provided.
This is best illustrated by example. Take the following very simple code snippet:
int a, b;
int getAPlusB()
{
return a + b;
}
The following is the result of arm-none-eabi-objdump -xdr test.o:
arm-none-eabi-gcc -c -Os -mthumb -mcpu=cortexm3 -mlittle-endian -o test.o test.c:
SYMBOL TABLE:
00000000 g F .text 0000000c getAPlusB
00000004 g O .bss 00000004 b
00000000 g O .bss 00000004 a
Disassembly of section .text:
00000024 <getAPlusB>:
24: 4b01 ldr r3, [pc, #4] ; (2c <getAPlusB+0x8>)
26: cb09 ldmia r3, {r0, r3}
28: 4418 add r0, r3
2a: 4770 bx lr
2c: 00000000 .word 0x00000000
2c: R_ARM_ABS32 .bss
arm-none-eabi-gcc -c -Os -ffunction-sections -fdata-sections \
-mthumb -mcpu=cortexm3 -mlittle-endian -o test.o test.c:
SYMBOL TABLE:
00000000 g F .text.getAPlusB 00000014 getAPlusB
00000000 g O .bss.b 00000004 b
00000000 g O .bss.a 00000004 a
Disassembly of section .text.getAPlusB:
00000000 <getAPlusB>:
0: 4b02 ldr r3, [pc, #8] ; (c <getAPlusB+0xc>)
2: 6818 ldr r0, [r3, #0]
4: 4b02 ldr r3, [pc, #8] ; (10 <getAPlusB+0x10>)
6: 681b ldr r3, [r3, #0]
8: 4418 add r0, r3
a: 4770 bx lr
...
c: R_ARM_ABS32 .bss.a
10: R_ARM_ABS32 .bss.b
The difference is subtle, but important. The flag enabled code performs two separate loads, while the disabled code performs a single "load multiple." The enabled code does this because it knows both symbols are contained in the same section, in a certain sequence. With the enabled code, this is not the case. The symbols are in two separate sections, and while it is likely they will keep their order and proximity, it is not guaranteed. What's more, if both sections are not referenced, the linker may decide one section is not used, and remove it.
Another example:
int a, b;
int getB()
{
return b;
}
And the generated code. First without the flags:
SYMBOL TABLE:
00000000 g F .text 0000000c getB
00000004 g O .bss 00000004 b
00000000 g O .bss 00000004 a
Disassembly of section .text:
00000018 <getB>:
18: 4b01 ldr r3, [pc, #4] ; (20 <getB+0x8>)
1a: 6858 ldr r0, [r3, #4]
1c: 4770 bx lr
1e: bf00 nop
20: 00000000 .word 0x00000000
20: R_ARM_ABS32 .bss
And with the flags:
SYMBOL TABLE:
00000000 g F .text.getB 00000014 getB
00000000 g O .bss.b 00000004 b
00000000 g O .bss.a 00000004 a
Disassembly of section .text.getB:
00000000 <getB>:
0: 4b01 ldr r3, [pc, #4] ; (8 <getB+0x8>)
2: 6818 ldr r0, [r3, #0]
4: 4770 bx lr
6: bf00 nop
8: 00000000 .word 0x00000000
8: R_ARM_ABS32 .bss.b
In this case, the difference is even more subtle. The enabled code loads with an offset of 0, while the disabled code uses 4. Since the disabled code references the beginning of the section, it needs to offset to the location of b. However the enabled code references the section which contains solely b, and therefore does not need an offset. If we were to split this and only change the relocation, the new code would contain a reference to the section a was in, but not b. This, again, could cause the linker to garbage collect the wrong section.
These were just two scenarios that I came across when looking at this problem, there may be more.
Producing valid object files functionally equivalent to code compiled with the -ffunction-sections and -fdata-sections flags would require parsing the machine instructions looking for these and any other relocation issues that could come up. This is not an easy task to accomplish.
I have the below instruction in ARM NEON code. Can you please tell me the equivalent in gcc?
label
DCFS 1.5
DCFS -1.4
I am not sure but i think the only way to do the above in gcc is using a table.
PLease let me know if there is an equivalent representation in gcc.
https://sourceware.org/binutils/docs/as/Float.html#Float
.float flonums
This directive assembles zero or more flonums, separated by commas. It has the same effect as .single.
I couldn't find any reference about alignment guarantees but from experiment it looks like there is none.
$ cat f.s
.data
.byte 0xaa
.float 3.14
.byte 0x55
.text
test:
mov r0, r1
$ as f.s -o f.o
$ objdump -s -j .data f.o
f.o: file format elf32-littlearm
Contents of section .data:
0000 aac3f548 4055 ...H#U
I am trying to build the following really small C program into a raw binary file:
asm ("call sys_main\n" // Immediately run sys_main at start of code
"__asm_loop_halt:\n"
"jmp __asm_loop_halt\n"); // Then halt
int sys_main() {
short *addr = (short*) 0x08b000; // Address start of EGA-VRAM
*addr = 0x0f41; // Write white 'A' on black to screen
}
Because I am trying to create a raw binary I have to use a linker script gcc -std=gnu99 -Os -nostdlib -m32 -march=i386 -ffreestanding -Wl,--nmagic,--script=386.ld -o test test.c:
OUTPUT_FORMAT(binary)
SECTIONS
{
.text 0x0500 :
{
*(.text);
}
.data :
{
*(.data);
*(.bss);
*(.rodata);
}
_heap = ALIGN(4);
}
The script is supposed to tell the linker that the code starts running at 0x500 and that it should only create a binary file. However when I disassemble the binary, I get:
00000000 E802000000 call dword 0x7
00000005 EBFE jmp short 0x5
00000007 55 push ebp
00000008 89E5 mov ebp,esp
0000000A 66C70500B0080041 mov word [dword 0x8b000],0xf41
-0F
00000013 5D pop ebp
00000014 C3 ret
00000015 0000 add [eax],al
00000017 001400 add [eax+eax],dl
......
Appearantly the linker still took 0x0 as the start address of the code and also added a bunch of random data behind the last senseful 'ret' instruction, that is in total 4 times as big as the code.
What is this data, why is it there and what did I do wrong to have my code start at 0x0?
Edit: Thanks to Eugene's tip with the map I discovered that the bytes behind the .text section are .eh_frame responsible for exception handling which can easily removed by calling gcc with -fno-asynchronous-unwind-tables.
If I define a local character array within a function and then use objdump to grab the assembly code for that particular function, can I find the memory for that array within the assembly code?
This is a question I have for a homework assignment.
Sure, as long as your array has a non-zero initializer, you should be able to find it. Here's an example I made for ARM:
char function(int i)
{
char arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
return arr[i];
}
Build it:
$ clang -O2 -Wall -c -o example.o example.c
Disassemble the output:
$ objdump -d example.o
example.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <function>:
0: e59f1004 ldr r1, [pc, #4] ; c <function+0xc>
4: e7d10000 ldrb r0, [r1, r0]
8: e12fff1e bx lr
c: 00000000 .word 0x00000000
Hmm - notice that .word 0x0000000 at offset 0xc? That's going to be fixed up by the linker to point to the array. Let's go check out the relocation table:
$ objdump -r example.o
example.o: file format elf32-littlearm
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
00000008 R_ARM_V4BX *ABS*
0000000c R_ARM_ABS32 .rodata.cst8
Aha! The word at 0xc is going to get fixed up with an absolute pointer to the .rodata.cst8 section - that sounds like what we want. Let's take a peek:
$ objdump -s -j .rodata.cst8 example.o
example.o: file format elf32-littlearm
Contents of section .rodata.cst8:
0000 01020304 05060708 ........
And there you have the contents of the array!
A local array is allocated on stack in run-time only (when the function is entered). So it doesn't present in executable.
An exception would be a static array.