当前位置: 首页 > 知识库问答 >
问题:

在区分源代码、目标代码、汇编代码和机器代码时,我感到困惑

汪辰阳
2023-03-14

我读了我们编写源代码的每一个地方(高级语言),编译器将其转换为机器代码(低级语言)。然后我读到有一个汇编程序,它将汇编代码转换为机器代码。然后在区分编译器和解释器时,我读到编译器首先将整个代码转换为目标代码,而解释器通过跳过目标代码直接转换为机器代码。现在我有困惑,我想到了以下问题:

  1. 从汇编代码出来的地方,编译器是否直接将源代码转换为机器代码?
  2. 目标代码和机器代码有什么区别?
  3. 谁将源代码转换为汇编代码?
  4. 什么是高级语言和低级语言,如何区分它们?
  5. 汇编代码和目标代码是高级还是低级?

共有3个答案

华欣怡
2023-03-14

除了源代码之外,所有语言都是低级语言。

我相信对象和机器代码指的是同一件事。

源代码到汇编代码没有直接转换,因为源代码通常直接转换为机器代码。汇编程序可用于将汇编代码转换为机器代码(汇编语言与机器代码具有1:1的对应关系)。编译器用于将源代码直接转换为机器代码。

使用汇编程序是因为,由于每种类型的计算机的机器代码不同,汇编语言也针对每种类型的计算机。

高级语言是我们将抽象的低级语言使用到易于阅读和理解的代码中的语言。它是一种抽象,可以帮助我们在编码时提高工作效率。

低级语言是指很少或根本没有从计算机指令集抽象出来的语言。

傅元章
2023-03-14

汇编程序采用汇编语言,即人类更容易读写的处理器指令,并将其转换为机器代码或这些指令的二进制版本。

汇编语言向量。s

.thumb

.globl _start
_start:
.word 0x20001000
.word reset
.word foo
.word foo
.word foo
.word foo
.word foo
.word foo

.thumb_func
reset:
    bl fun
.thumb_func
foo:
    b foo

.globl dummy
dummy:
    bx lr

组装然后拆卸

arm-none-eabi-as vectors.s -o vectors.o
arm-none-eabi-objdump -D vectors.o > vectors.list

拆卸的相关部分

Disassembly of section .text:

00000000 <_start>:
   0:   20001000
    ...

00000020 <reset>:
  20:   f7ff fffe   bl  0 <fun>

00000024 <foo>:
  24:   e7fe        b.n 24 <foo>

00000026 <dummy>:
  26:   4770        bx  lr

这个文字不是指令,而是将数据放入二进制/输出的方式。在本例中,我将生成一个向量表。反汇编程序尚未显示所有内容,我们将看到其余内容。汇编器留下了占位符,我们将很快看到这些占位符,供链接器填写。这就是一个对象看起来的样子,程序集已经被转换为机器代码。组件bx lr,机器代码0x4770

该规则也有例外,通常是出于特定原因,但让编译器直接编译为机器代码通常没有意义。你必须有一个目标的汇编程序,所以它已经存在了,请使用它。编译器编写器调试汇编代码要比调试机器代码容易得多。也有一些例外,比如“仅仅因为我想”有点像为什么你要爬山而不是四处转转“因为它就在那里”。还有就是及时的原因,还有其他一些原因。JIT需要更快地获得机器代码,或者使用一个工具/库/驱动程序等。。。所以你可能会看到那些跳过这一步的人,这是很难发展的。通常,你可以通过重命名你的汇编程序来测试这个理论(但必须使用正确的二进制,你在命令行上运行的那个可能是真正的汇编程序的前端,实际上在gcc的情况下,我认为我们使用的gcc程序只是cc1的前端,可能还有另外一两个程序以及汇编程序和链接程序,除非你告诉它不要这样做,否则所有这些都是从gcc派生出来的)。

所以我们用简单的进入程序

#define FIVE 5
unsigned int more_fun ( unsigned int );
void fun ( void )
{
    more_fun(FIVE);
}

编写

arm-none-eabi-gcc -mthumb -save-temps -O2 -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o > fun.list

第一个临时是预处理器,采用#定义和#包含并基本上摆脱它们,生成将发送到编译器的文件

# 1 "fun.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "fun.c"


unsigned int more_fun ( unsigned int );
void fun ( void )
{
    more_fun(5);
}

然后调用编译器本身并将其编译为汇编语言

    .cpu arm7tdmi
    .fpu softvfp
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .code   16
    .file   "fun.c"
    .text
    .align  2
    .global fun
    .code   16
    .thumb_func
    .type   fun, %function
fun:
    push    {r3, lr}
    mov r0, #5
    bl  more_fun
    @ sp needed
    pop {r3}
    pop {r0}
    bx  r0
    .size   fun, .-fun
    .ident  "GCC: (15:4.9.3+svn231177-1) 4.9.3 20150529 (prerelease)"

然后调用汇编器将其转换为一个对象,我们可以在这里的对象分解中看到所生成的内容:

Disassembly of section .text:

00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   2005        movs    r0, #5
   4:   f7ff fffe   bl  0 <more_fun>
   8:   bc08        pop {r3}
   a:   bc01        pop {r0}
   c:   4700        bx  r0
   e:   46c0        nop         ; (mov r8, r8)

现在bl 0还没有实现,more\u fun是一个外部标签,所以链接器必须进来并修复这个问题,我们很快就会看到。

更多乐趣。c相同的故事

源代码

#define ONE 1
unsigned int more_fun ( unsigned int x )
{
    return(x+ONE);
}

编译器输入

# 1 "more_fun.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "more_fun.c"


unsigned int more_fun ( unsigned int x )
{
    return(x+1);
}

编译器输出(汇编程序输入)

    .cpu arm7tdmi
    .fpu softvfp
    .eabi_attribute 20, 1
    .eabi_attribute 21, 1
    .eabi_attribute 23, 3
    .eabi_attribute 24, 1
    .eabi_attribute 25, 1
    .eabi_attribute 26, 1
    .eabi_attribute 30, 2
    .eabi_attribute 34, 0
    .eabi_attribute 18, 4
    .code   16
    .file   "more_fun.c"
    .text
    .align  2
    .global more_fun
    .code   16
    .thumb_func
    .type   more_fun, %function
more_fun:
    add r0, r0, #1
    @ sp needed
    bx  lr
    .size   more_fun, .-more_fun
    .ident  "GCC: (15:4.9.3+svn231177-1) 4.9.3 20150529 (prerelease)"

对象的反汇编(汇编程序输出)

Disassembly of section .text:

00000000 <more_fun>:
   0:   3001        adds    r0, #1
   2:   4770        bx  lr

现在我们将所有这些链接在一起(这就是为什么称之为工具链的原因,编译、组装、链接一系列链接在一起的工具,其中一个工具的输出提供另一个工具的输入)

arm-none-eabi-ld -Ttext=0x2000 vectors.o fun.o more_fun.o -o run.elf
arm-none-eabi-objdump -D run.elf > run.list
arm-none-eabi-objcopy -O srec run.elf run.srec


Disassembly of section .text:

00002000 <_start>:
    2000:   20001000 
    2004:   00002021 
    2008:   00002025 
    200c:   00002025 
    2010:   00002025 
    2014:   00002025 
    2018:   00002025 
    201c:   00002025 

00002020 <reset>:
    2020:   f000 f802   bl  2028 <fun>

00002024 <foo>:
    2024:   e7fe        b.n 2024 <foo>

00002026 <dummy>:
    2026:   4770        bx  lr

00002028 <fun>:
    2028:   b508        push    {r3, lr}
    202a:   2005        movs    r0, #5
    202c:   f000 f804   bl  2038 <more_fun>
    2030:   bc08        pop {r3}
    2032:   bc01        pop {r0}
    2034:   4700        bx  r0
    2036:   46c0        nop         ; (mov r8, r8)

00002038 <more_fun>:
    2038:   3001        adds    r0, #1
    203a:   4770        bx  lr

链接器已经调整了外部标签,在这种情况下,通过修改指令以获得正确的偏移量。

   4:   f7ff fffe   bl  0 <more_fun>
202c:   f000 f804   bl  2038 <more_fun>

elf文件格式是一种“二进制”文件,它是二进制的,因为您可以使用文本编辑器打开它,您会看到一些文本,但大部分是垃圾。还有其他的“二进制”文件格式,如摩托罗拉s-record,在这种情况下,它只包括真实的东西、机器代码和任何数据,其中elf包含诸如字符串“fun”、“more\u fun”等调试信息,而反汇编程序恰好使用这些信息使输出更漂亮。Motorola S-Record和Intel Hex是如下ascii文件格式:

S00B000072756E2E73726563C4
S113200000100020212000002520000025200000D1
S113201025200000252000002520000025200000A8
S113202000F002F8FEE7704708B5052000F004F858
S10F203008BC01BC0047C04601307047EA
S9032000DC

不再经常使用,但并非完全无用,过去需要这种格式来编程rom,工具制造商个人偏好他们支持的文件格式。二进制文件是如何在微控制器中烧成闪存的?一些工具从主机/开发机器上获取这些位,并通过一些接口和一些软件将其移动到目标,该工具支持什么二进制文件格式?由编写该工具的人来选择一种或多种格式。

在编译器以各种方式(购买成本和/或在计算机上保存程序的存储空间,加上中间数据等)价格合理之前,汇编程序可以用来生成整个程序。您可以看到这样的指令。org 100h中,有了“工具链”,汇编程序可能具有该功能,但作为该链的一部分,汇编工具需要从汇编语言转换为对象格式,大部分转换为机器代码和其他数据。当然,当工具链的一部分sane方法最终从源代码到汇编语言时,编译器可能完成所有工作并输出完成的二进制文件。我们使用的编译器工具,gcc、msvc、clang等,除非被告知不要太多,否则将为我们生成汇编程序和链接器,以及编译器,使编译器似乎在一个神奇的步骤中从源代码到最终二进制。链接器获取具有未html" target="_blank">解析外部标签的单个对象,并决定它们在内存图像中的位置,在内存中的位置,根据需要解析外部。链接器所做的工作在很大程度上是这些工具系统设计的一部分,这种设计可以使链接器不修改单个指令,而只将地址放在约定的位置。例如:

向量。s

.globl _start
_start:
    bl fun
    b .
.global hello
hello: .word 0

趣. c

#define FIVE 5
extern unsigned int hello;
void fun ( void )
{
    hello+=FIVE;
}

趣. o拆卸

Disassembly of section .text:

00000000 <fun>:
   0:   e59f200c    ldr r2, [pc, #12]   ; 14 <fun+0x14>
   4:   e5923000    ldr r3, [r2]
   8:   e2833005    add r3, r3, #5
   c:   e5823000    str r3, [r2]
  10:   e12fff1e    bx  lr
  14:   00000000    andeq   r0, r0, r0

所以我们可以看到它正在从偏移量/地址0x14加载一个数字到r2,然后该数字用作地址来获取hello,然后读取的内容添加了5,然后r2中的地址用于将hello保存回内存。所以0x14处是编译器留下的占位符,因此链接器可以将地址放置在那里,我们可以看到链接后

Disassembly of section .text:

00002000 <_start>:
    2000:   eb000001    bl  200c <fun>
    2004:   eafffffe    b   2004 <_start+0x4>

00002008 <hello>:
    2008:   00000000    andeq   r0, r0, r0

0000200c <fun>:
    200c:   e59f200c    ldr r2, [pc, #12]   ; 2020 <fun+0x14>
    2010:   e5923000    ldr r3, [r2]
    2014:   e2833005    add r3, r3, #5
    2018:   e5823000    str r3, [r2]
    201c:   e12fff1e    bx  lr
    2020:   00002008    andeq   r2, r0, r8

0x2020现在保存着hello的地址,编译器构建了这个程序,这样链接器就可以轻松地填写这个地址,链接器也可以填写它。当然可以使用分支/跳转地址来实现这一点,来自同一工具的不同工具链或不同目标将产生不同的解决方案,这通常与指令集有关。您有一个具有近调用(相对)和远调用(绝对)的调用,您是否使用远调用编译外部,使其始终工作?或者,你是否会抓住机会,建立一个近距离的联系,并冒着链接者不得不放蹦床的风险?

虽然不是那么精确,但我可以让gcc很容易地为拇指/手臂做到这一点。

.thumb
.globl _start
_start:
    bl fun
    b .
.global hello
hello: .word 0


#define FIVE 5
extern unsigned int hello;
void fun ( void )
{
    hello+=FIVE;
}

链接二进制的反汇编

00002000 <_start>:
    2000:   f000 f812   bl  2028 <__fun_from_thumb>
    2004:   e7fe        b.n 2004 <_start+0x4>

00002006 <hello>:
    2006:   00000000    andeq   r0, r0, r0
    ...

0000200c <fun>:
    200c:   e59f200c    ldr r2, [pc, #12]   ; 2020 <fun+0x14>
    2010:   e5923000    ldr r3, [r2]
    2014:   e2833005    add r3, r3, #5
    2018:   e5823000    str r3, [r2]
    201c:   e12fff1e    bx  lr
    2020:   00002006    andeq   r2, r0, r6
    2024:   00000000    andeq   r0, r0, r0

00002028 <__fun_from_thumb>:
    2028:   4778        bx  pc
    202a:   46c0        nop         ; (mov r8, r8)
    202c:   eafffff6    b   200c <fun>

因为这个特定指令集的工作方式你不能使用bl(基本上是call)指令从拇指代码到arm代码,你必须使用bx,它只是一个分支(跳转)而不是调用,链接器放置了一个蹦床,一些代码用于为我们从一件事反弹到另一件事。

并非所有指令集都易于拆解和/或工具链不包括一个,它不是工具链的必需部分。但是您可以并且应该使用gnu和其他工具对此或其他目标重复此操作,正如您所看到的,我不必拥有特殊的硬件,我不必编写十几行代码即可查看这些工具的工作情况。

顾高扬
2023-03-14

大多数问题都没有简单的答案,因为每个编译器的答案都可能不同。一些编译器发出其他高级语言,如C。

>

int main(){返回1;}

命令

gcc-v-o测试。C

输出(我已经过滤了很多)

cc1 test.c -o /tmp/cc9Otd7R.s
as -v --64 -o /tmp/cc5KhWEM.o /tmp/cc9Otd7R.s
collect2 --eh-frame-hdr -m elf_x86_64 -o test /tmp/cc5KhWEM.o

48 83 ec 10子rsp,0x10

前四个字是机器代码的4个字节,后面是汇编程序。

根据第1点,这将是编译器后端。

和5。这有点主观,但装配水平较低。您通常不会手动修改对象代码(有时我会使用十六进制编辑器进行修改,但这种更改通常非常小)

 类似资料:
  • 这可能吗?我所做的更改需要在服务器上被识别,以便更改不仅仅发生在我的计算机上。此外,我甚至不知道是否可以在不下载外部插件的情况下查看实际的源代码(由于某些原因,外部插件无法工作)。有人做过这样的事吗?

  • 你马上就要写下第一行代码,现在该下载一个代码编辑器了! 注意在之前章节你可能已经完成了这一步,如果那样的话,你可以直接进入下一章节。 有很多不同的编辑器,通常根据个人偏好选择。 大部分 Python程序员使用像 PyCharm这样复杂但是功能强大的IDE (集成开发环境)。 然而,这可能不太适合初学者。我们建议使用同样强大但是更为简单的编辑器。 下面是我们的建议,但是你可以随时咨询你的教练。那样会

  • 程序员接触时间最长的就是代码编辑器。 代码编辑器主要分两种:IDE(集成开发环境)和轻量编辑器。很多人喜欢这两种各选一个。 IDE IDE(集成开发环境)是指用于管理整个项目的,具有强大功能的编辑器。顾名思义,它不仅仅是一个编辑器,而且还是个完整的“开发环境”。 IDE 加载项目(通常包含很多文件),并且允许在不同文件之间导航(navigation)。IDE 还提供基于整个项目(不仅仅是打开的文件

  • 因此,通常关于通过汇编代码提高性能的问题的答案是“不要打扰,编译器比你聪明”。我明白了。 但是,我注意到优化的线性代数库(例如ACML)可以比标准编译库实现2到5倍的性能改进。例如,在我的8核机器上,与现有的单线程BLAS实现相比,优化的矩阵乘法运行速度快了30倍以上,这意味着,在考虑了由于使用所有内核而提高的8倍之后,仅仅通过优化仍然可以提高4倍。 所以在我看来,优化的汇编代码确实可以带来巨大的

  • 编译solidity源代码 通过solc编译器编译solidity代码到字节码。你可以参考solidity安装文档在本地安装编译器。 编写solidity代码运行: $ solc <contract>.sol --bin --abi --optimize -o <output-dir>/ bin和ABI编译器参数都需要充分利用web3j中的智能合约。 bin,输出包含十六进制编码的solidit

  • 我试图在一个项目上执行一些PHP代码(使用Dreamweaver),但代码没有运行。 当我检查源代码时,PHP代码显示为HTML标记(我可以在源代码中看到)。Apache运行正常(我正在使用XAMPP),PHP页面打开正常,但PHP代码没有执行。 有人对正在发生的事情有什么建议吗? 编辑:代码..: