如何获得 C 函数的基本编译二进制代码?
how to get the bare bone compiled binary code of a C function?
我正在尝试在 stm32 F4 micro 上实现一个嵌入式固件,它从串口获取二进制代码并在 micro 上执行它。
这个想法很简单,当然唯一棘手的部分是,因为在串行上发送原始二进制数据很复杂,我将通过 base64 编码发送所有内容。
代码如下:
#include <Arduino.h>
#include <base64.hpp>
size_t read_serial_line(char *msg, size_t len, size_t timeout = 0) {
const auto start = millis();
size_t sz = 0;
do {
while (not Serial.available()) {
if (timeout > 0 and millis() > start + timeout) {
return -1;
}
}
msg[sz] = Serial.read();
if (msg[sz] == '\r') {
msg[sz] = '[=10=]'; // replacing the end line with the end string
// the next char must be a \n char since the Serial.println of arduino
// works like that
while (Serial.read() != '\n')
; // I discard it
// now sz contains the length of the string as returned by strlen
break; // end of line
}
if (timeout > 0 and millis() > start + timeout) {
return -1;
}
} while (++sz < len);
return sz;
}
void setup() {
Serial.begin(9600);
Serial.println("begin!");
}
void loop() {
char *msg = new char[2048](); // big line
auto sz = read_serial_line(msg, 2048);
Serial.print("\tlooping...");
Serial.println(sz);
Serial.print("received: ");
Serial.println(msg);
uint8_t *code = new uint8_t[2048]();
sz = decode_base64(msg, code);
Serial.println(sz);
delay(1000);
int (*code_fn)() = (int (*)())code;
int c = code_fn();
Serial.println(c);
delete code;
delete msg;
delay(1000);
}
下一个问题是能够编译并从这个简单的 C 函数中获取编译后的二进制代码:
int fn() {
return 3;
}
在这里你可以看到assembly这个愚蠢的功能。
我尝试过,当然是使用用于 micro 主代码的相同工具链,使用与位置无关代码的选项使用 gcc 编译它,然后我尝试使用 objcopy 复制 .text 部分,最后,我获取了 xxd 命令返回的文本,将其编码为 base64 并将其发送到 micro。
以下是我使用的命令:
$ arm-none-eabi-gcc -fPIC -c test.c
$ arm-none-eabi-objcopy -j .text test.o test.bin
$ xxd -p test.bin
如我所料,这个想法行不通,我的假设是我从这个过程中得到的不仅仅是函数的二进制代码。
我有这个想法是因为输出文件 test.bin 相当大,有 440 个字节,在我看来对于 7 条汇编指令来说有点太多了。
这就是我提出问题的原因:如何获取二进制代码并且只获取该代码?
您不小心生成了 ELF 文件而不是简单的 BIN 文件。 (您可以使用 file
实用程序验证这一点,如果您的系统有的话。)
要从您的代码生成一个小的 BIN 文件,请将您的第二个命令更改为:
arm-none-eabi-objcopy -j .text test.o -O binary test.bin
请注意,当您执行通过串行线路接收的任意机器代码时,可能会出现大量复杂情况和安全问题。我不建议将其作为设计,只是尝试回答您提出的问题。
我正在尝试在 stm32 F4 micro 上实现一个嵌入式固件,它从串口获取二进制代码并在 micro 上执行它。
这个想法很简单,当然唯一棘手的部分是,因为在串行上发送原始二进制数据很复杂,我将通过 base64 编码发送所有内容。
代码如下:
#include <Arduino.h>
#include <base64.hpp>
size_t read_serial_line(char *msg, size_t len, size_t timeout = 0) {
const auto start = millis();
size_t sz = 0;
do {
while (not Serial.available()) {
if (timeout > 0 and millis() > start + timeout) {
return -1;
}
}
msg[sz] = Serial.read();
if (msg[sz] == '\r') {
msg[sz] = '[=10=]'; // replacing the end line with the end string
// the next char must be a \n char since the Serial.println of arduino
// works like that
while (Serial.read() != '\n')
; // I discard it
// now sz contains the length of the string as returned by strlen
break; // end of line
}
if (timeout > 0 and millis() > start + timeout) {
return -1;
}
} while (++sz < len);
return sz;
}
void setup() {
Serial.begin(9600);
Serial.println("begin!");
}
void loop() {
char *msg = new char[2048](); // big line
auto sz = read_serial_line(msg, 2048);
Serial.print("\tlooping...");
Serial.println(sz);
Serial.print("received: ");
Serial.println(msg);
uint8_t *code = new uint8_t[2048]();
sz = decode_base64(msg, code);
Serial.println(sz);
delay(1000);
int (*code_fn)() = (int (*)())code;
int c = code_fn();
Serial.println(c);
delete code;
delete msg;
delay(1000);
}
下一个问题是能够编译并从这个简单的 C 函数中获取编译后的二进制代码:
int fn() {
return 3;
}
在这里你可以看到assembly这个愚蠢的功能。
我尝试过,当然是使用用于 micro 主代码的相同工具链,使用与位置无关代码的选项使用 gcc 编译它,然后我尝试使用 objcopy 复制 .text 部分,最后,我获取了 xxd 命令返回的文本,将其编码为 base64 并将其发送到 micro。
以下是我使用的命令:
$ arm-none-eabi-gcc -fPIC -c test.c
$ arm-none-eabi-objcopy -j .text test.o test.bin
$ xxd -p test.bin
如我所料,这个想法行不通,我的假设是我从这个过程中得到的不仅仅是函数的二进制代码。 我有这个想法是因为输出文件 test.bin 相当大,有 440 个字节,在我看来对于 7 条汇编指令来说有点太多了。
这就是我提出问题的原因:如何获取二进制代码并且只获取该代码?
您不小心生成了 ELF 文件而不是简单的 BIN 文件。 (您可以使用 file
实用程序验证这一点,如果您的系统有的话。)
要从您的代码生成一个小的 BIN 文件,请将您的第二个命令更改为:
arm-none-eabi-objcopy -j .text test.o -O binary test.bin
请注意,当您执行通过串行线路接收的任意机器代码时,可能会出现大量复杂情况和安全问题。我不建议将其作为设计,只是尝试回答您提出的问题。