【问题标题】:How does an assembler place a constant in a file as data, and how is it executed that way?汇编程序如何将常量作为数据放入文件中,以及如何以这种方式执行?
【发布时间】:2013-07-26 21:16:02
【问题描述】:

例如,在 Assembly x86 中,我们可以使用 .data 条目部分并像这样静态定义数据字节:

MSG db 'CAGA', AAFF

我的问题是,假设我们正在组装成一个平面二进制 (bin) 文件,汇编器如何或做什么来实现将数据插入到二进制文件中。

我想知道,因为我正在尝试反编译,并更好地了解如何使用机器代码编程。

看,我想用机器码编写系统软件,但汇编器抽象出一些机器码概念(如静态数据声明、对齐、指令宽度、语句的结构、操作数或一般代码),我在停顿。

我只是想问一下,关于机器代码,它是如何在这些基础上进行布局的:

程序的 .data 部分是如何静态添加到文件中的,当 CPU 获取指令时,它如何在运行时/处理时使用?例如,在下面这个程序中,它是一个 x86 引导加载程序,在 FASM 上采用 Intel 语法汇编代码,

    [BITS 16]   ;Tells the assembler that its a 16 bit code
[ORG 0x7C00]    ;Origin, tell the assembler that where the code will
                ;be in memory after it is been loaded

MOV SI, HelloString ;Store string pointer to SI
CALL PrintString    ;Call print string procedure
JMP $       ;Infinite loop, hang it here.


PrintCharacter: ;Procedure to print character on screen
    ;Assume that ASCII value is in register AL
MOV AH, 0x0E    ;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00    ;Page no.
MOV BL, 0x07    ;Text attribute 0x07 is lightgrey font on black background

INT 0x10    ;Call video interrupt
RET     ;Return to calling procedure



PrintString:    ;Procedure to print string on screen
    ;Assume that string starting pointer is in register SI

next_character: ;Lable to fetch next character from string
MOV AL, [SI]    ;Get a byte from string and store in AL register
INC SI      ;Increment SI pointer
OR AL, AL   ;Check if value in AL is zero (end of string)
JZ exit_function ;If end then return
CALL PrintCharacter ;Else print the character which is in AL register
JMP next_character  ;Fetch next character from string
exit_function:  ;End label
RET     ;Return from procedure


;Data
HelloString db 'Hello World', 0 ;HelloWorld string ending with 0

TIMES 510 - ($ - $$) db 0   ;Fill the rest of sector with 0
DW 0xAA55           ;Add boot signature at the end of bootloader

"HelloString db 'Hello World', 0" 作为 0 和 1 静态插入到 bin 文件中,但是在机器代码中,如何将静态二进制数据作为操作数添加通过将字符串指针地址存储到寄存器到 MOV SI 指令?

基本上,文件中的静态二进制数据字节如何作为代码操作数被移入源索引寄存器?

【问题讨论】:

    标签: assembly


    【解决方案1】:

    MOV SI, HelloString

    是真正的简写

    MOV SI, OFFSET HelloString

    实际的字符串 HelloString 被放置在二进制流中,就像代码作为字节序列一样。对于分段代码,它通常位于单独的数据段中,可以根据平台改变名称。对于 COM 文件之类的文件,数据通常紧跟在代码区域之后,通常从适当对齐的地址开始。

    更新:

    我现在无法访问 16 位系统,但下面是 32 位 x86 代码中的类似内容:

    Binary code    Instruction
    BEA43F4600     mov esi, offset @HelloString  
    E8F2FFFFFF     call PrintString
    EBFE           @1: jmp @1
    48656C6C6F     @HelloString: "Hello"
    

    当然@HelloString 的实际偏移量和PrintString 的实际地址取决于上下文。

    【讨论】:

    • 我不太明白 - 你能在图片中定义还是更深入地定义?
    【解决方案2】:

    您的问题的答案取决于您的二进制文件的执行方式。

    假设目前使用像 Linux 或 MS-windows 这样的操作系统, 二进制文件很复杂,带有头信息等。让我们忽略它并 专注于程序在内存中后的外观。

    稍微简单一点。

    代码区

    代码前奏

    MOV AL,BL ...

    代码后奏

    数据区

    我的数据: 数据“caga”,0AH

    该区域传统上称为分段。现在这个名字被英特尔劫持了,它们被微软和 linux (ELF) 称为部分。虽然有很强的关系。 MOV 是您的程序开始的地方,但需要设置前奏 和一个后奏曲(或者你永远不会再回到 Linux 了)。 在 Pentium 上的典型 Linux/Windows 情况下,您会看到 Intel 段硬件支持这些部分。底线是,您将无法跳转到 MYDATA,即将程序计数器设置为包含 MYDATA。相反,硬件会在 linux 上处理“分段错误”,在 Windows 上可能会出现蓝屏。

    可以肯定的是,您可以执行“数据”,因为实际上代码只是在正确位置和时间执行的数据。例如。您可以通过放入类似的行来强制执行以下内容 SECTION .text

    (愚蠢,我知道。我不是在编造这个。)

    在汇编程序代码部分你可以说 MOV AL,BL 它会将 0x88 和 0xD8 放在代码段中的两个连续字节中。 如果你把它也可以有同样的效果

    DB 0x88, 0xD8

    在您的程序中完全相同的位置。

    您会明白,上述内容已大大简化。例如。一个典型的 ELF 二进制文件可以很容易地包含 20 个部分。它对英特尔来说也有些具体,但具体比抽象更容易理解。

    您的示例显然是一个引导情况。它不完整,因为缺少 HelloString。现在,如果我们将它添加到程序的末尾,HelloString 会被记住为一个地址,即一个数字。在组装 MOV SI,.. 指令时,留下了一个孔来填充该数字。在处理整个文件一次后,HelloString 地址是已知的,并且汇编器将其填充。

    格罗耶斯·阿尔伯特

    【讨论】:

    • 从技术上讲,我只是想知道这在裸机上是如何工作的(即启动后,从单个文件执行指令;没有操作系统、内存管理、内核等)。只是为了简单起见,但无论如何谢谢。
    • 请注意文件只存在于操作系统中。我已经编辑了我的答案以更具体地针对您的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 2020-02-11
    • 1970-01-01
    相关资源
    最近更新 更多