Assembly 如何理解这一点?

Assembly 如何理解这一点?,assembly,objcopy,Assembly,Objcopy,这是我的 test.o和test.bin之间有什么区别 .text call start str: .string "test\n" start: movl $4, %eax movl $1, %ebx pop %ecx movl $5, %edx int $0x80 ret 上面的操作是什么?.o是预链接器,.bin是后链接器。 以下是一篇关于链接器的维基百科文章: )

这是我的

test.o
test.bin
之间有什么区别

.text
    call start
    str:
        .string "test\n"
    start:
    movl    $4, %eax
    movl    $1, %ebx
    pop     %ecx
    movl    $5, %edx
    int     $0x80
    ret

上面的操作是什么?

.o是预链接器,.bin是后链接器。
以下是一篇关于链接器的维基百科文章: )
我相信您可以从这里得到要点:)

objcopy-O binary
复制源文件的内容。这里,
test.o
是一个“可重新定位的对象文件”:即代码,也是一个符号表和重新定位信息,它允许该文件与其他文件链接到一个可执行程序中。由
objcopy
生成的
test.bin
文件仅包含代码,不包含符号表或重定位信息。这样一个“原始”文件对于“正常”编程来说是无用的,但是对于有自己加载程序的代码来说却很方便

我假设您在32位x86系统上使用Linux。您的
test.o
文件大小为515字节。如果您尝试
objdump-x test.o
,您将得到以下内容,其中描述了
test.o
对象文件的内容:

$ objdump -x test.o

test.o:     file format elf32-i386
test.o
architecture: i386, flags 0x00000010:
HAS_SYMS
start address 0x00000000

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000001e  00000000  00000000  00000034  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000000  00000000  00000000  00000054  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  00000054  2**2
                  ALLOC
SYMBOL TABLE:
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
0000000b l       .text  00000000 start
00000005 l       .text  00000000 str
这给了你很多信息。特别是,该文件包含一个名为
.text
的节,从文件中的偏移量0x34开始(十进制为52),长度为0x1e字节(十进制为30)。您可以将其分解以查看操作码本身:

$ objdump -d test.o

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <str-0x5>:
   0:   e8 06 00 00 00          call   b <start>

00000005 <str>:
   5:   74 65                   je     6c <start+0x61>
   7:   73 74                   jae    7d <start+0x72>
   9:   0a 00                   or     (%eax),%al

0000000b <start>:
   b:   b8 04 00 00 00          mov    $0x4,%eax
  10:   bb 01 00 00 00          mov    $0x1,%ebx
  15:   59                      pop    %ecx
  16:   ba 05 00 00 00          mov    $0x5,%edx
  1b:   cd 80                   int    $0x80
  1d:   c3                      ret    
我们在这里准确地识别了
test.o
.text
部分的30个字节。这就是
objcopy-O binary
所做的:它提取文件内容,即唯一的非空部分,即原始操作码本身,删除所有其他内容,特别是符号表和重定位信息

重新定位是指必须在给定的代码段中更改哪些内容,以便它在存储在内存中的给定位置时能够正常运行。例如,如果代码使用一个变量并希望获得该变量的地址,那么重新定位信息将包含一个条目,告诉实际将代码放入内存的人(通常是链接器):“在代码中,当您知道变量实际位于何处时,写入变量地址”。有趣的是,您显示的代码不需要重新定位:字节序列可以在任意内存位置写入并按原样执行

让我们看看代码的作用

  • 调用
    操作码跳转到偏移量0x0b处的
    mov
    指令。此外,由于这是一个
    调用
    ,它将返回地址推送到堆栈上。返回地址是调用完成后应继续执行的位置,即当到达
    ret
    操作码时。这是
    调用
    操作码后的字节地址。这里,该地址是文本字符串
    “test\n”
    的第一个字节的地址
  • 两个
    movl
    分别加载
    %eax
    %ebx
    ,数值分别为4和1
  • pop
    操作码从堆栈中删除顶部元素,并将其存储在
    %ecx
    中。这个顶级元素是什么?这正是
    调用
    操作码推送到堆栈上的地址,即文本字符串的第一个字节的地址
  • 第三个
    movl
    加载数值为5的
    %edx
  • int$0x80
    是32位x86 Linux上的系统调用:它调用内核。内核将查看寄存器以知道该做什么。内核首先查看
    %eax
    以获取“系统调用号”;在32位x86上,“4”是
    \uu NR\u write
    ,即
    write()
    系统调用。此调用需要三个参数,分别位于寄存器
    %ebx
    %ecx
    %edx
    中。它们是目标文件描述符(这里是1:这是标准输出)、指向要写入的数据的指针(这里是文本字符串)和要写入的数据的长度(这里是5,对应于四个字母和换行符)。因此,这会在标准输出上写入
    “test\n”
  • 最后的
    ret
    返回给调用者
    ret
    从堆栈中弹出一个值,并跳到该地址。这假设此代码块是通过调用操作码调用的
因此,总而言之,代码打印出带有换行符的
test

让我们使用自定义加载程序进行尝试:

#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

int
main(void)
{
        void *p;
        int f;

        p = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        f = open("test.bin", O_RDONLY);
        read(f, p, 30);
        close(f);
        mprotect(p, 30, PROT_READ | PROT_EXEC);
        ((void (*)(void))p)();
        return 0;
}

这就是我们所期望的:test.bin中的代码在标准输出上写出
test

Think.o是前置链接器,.bin是后置链接器-linker@Elad拉赫米,链接器前后的区别是什么?维基百科关于链接器的文章:为什么不直接通过
gcc-o test.bin test.s
生成
bin
?我不使用gcc,只是回答了你关于.o和.bin之间差异的问题:)这就是问题所在,不是吗?
.bin
是任意的;在Linux下,它并不意味着任何特定的东西,比如“post linker”。并且
objcopy
不是链接器;生成的文件与链接器生成的文件完全不同。Ok。很高兴知道。很抱歉造成混淆。可能您的系统上没有安装它?在基于Debian的Linux系统上,它附带了“bsdmainutils”包。您还可以使用
od
(来自“coreutils”,在Linux系统上可能比
hd
更频繁):
od-t x1 test.bin
$ hd test.bin
00000000  e8 06 00 00 00 74 65 73  74 0a 00 b8 04 00 00 00  |.....test.......|
00000010  bb 01 00 00 00 59 ba 05  00 00 00 cd 80 c3        |.....Y........|
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

int
main(void)
{
        void *p;
        int f;

        p = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        f = open("test.bin", O_RDONLY);
        read(f, p, 30);
        close(f);
        mprotect(p, 30, PROT_READ | PROT_EXEC);
        ((void (*)(void))p)();
        return 0;
}
$ ./blah
test