Задать вопрос
@blecked88

Как корректно вывести функцию в байтовом виде?

Хочу реализовать нечто такое:

#include <stdio.h>
#include <stdint.h>

typedef unsigned char uchar;

int f(int a, int b) {
    return a + b;
}
void _end(void) {};


int main(void) {
    uchar* start = f, *end = _end;
    int size = (int)(end - start);
    printf("size = %i\n", size);
    for (int i = 0; i < size; ++i) {
        printf("%02X ", *(start + i));
    }

    return 0;
}


однако данная программа либо вообще ничего не выдает (size < 0), либо выдает какие то неотносящиеся к данной функции байты.

реализовать надо так, чтобы работало на ЛЮБОМ компиляторе. Как это можно сделать?
  • Вопрос задан
  • 188 просмотров
Подписаться 1 Средний 4 комментария
Решения вопроса 1
@blecked88 Автор вопроса
Немного креатива и очень даже получилось написать код, работающий как на MSVC, так и на gcc (с разными флагами оптимизации).

Прикрепляю решение:

#pragma once

#include <stdio.h>

#define RET 0xC3 // Опкод инструкции ret

typedef unsigned char byte;

byte* AF_address(byte* f) {
    byte* real_address = NULL;
    #ifdef _MSC_VER // MSVC
        //printf("MSVC Compiler Detected\n");
        #ifdef NDEBUG // MSVC release mode
            real_address = f;
            //printf("FUNCTION ADDRESS: %p\n", real_address);
        #else // MSVC debug mode
            byte* f_p = f;
            byte* offset = (byte*)(*((int*)f_p) >> 8);
            real_address = f_p + (int)offset + 5;
            //printf("TABLE ADDRESS: %p\n", f_p);
            //printf("OFFSET: %p\n", offset);
            //printf("REAL ADDRESS: %p\n", real_address);
        #endif
    #elif defined(__GNUC__) // GCC
            //printf("GCC Compiler Detected\n");
            real_address = f;
            //printf("FUNCTION ADDRESS: %p\n", real_address);
    #endif
    return real_address;
}

// Печатает байты от некоторого address до address+size
int AF_print_bytes(byte* a, int size) {
    for (int i = 0; i < size; ++i) printf("%02X ", *(a + i));
}
// Возвращает размер произвольной функции в байтах
int AF_size(byte* f) {
    byte* p = f;
    for (; *p != RET; ++p);
    return p - f + 1;
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 6
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Переписать всё на ассемблер.
Компилятор C/C++ может использовать оптимизацию, менять порядок функций, делать функции инлайновыми.
Ответ написан
AshBlade
@AshBlade
Просто хочу быть счастливым
Я назвал 2 функцию не _end, а g и сработало. Возможно, gcc как-то по особенному интерпретирует это название - при дизассемблировании функции _end, даже не было.

Но тебе уже сказали, что компилятор может сделать все, что угодно и гарантировать расположение/вид функций нельзя.
Ответ написан
Комментировать
15432
@15432
Системный программист ^_^
Ну почему же ничего не выдает, size < 0 означает, что функция _end расположена перед функцией f, и это нормально, потому что часто компилятор соблюдает алфавитный порядок.
Назовите их func_a и func_b и попробуйте ещё раз. Обязательно используйте в коде эти функции, чтобы они не оказались вырезанными
Ответ написан
Комментировать
@res2001
Developer, ex-admin
Что мешает предусмотреть варианты когда f < _end и f > _end?
https://godbolt.org/z/8M33E4PYo
Но это не гарантирует, что компилятор чего-нибудь не вставит между функциями. О чем и говорит итоговый выхлоп.
И еще выхлоп может быть разным при разных флагах оптимизации: -O0/1/2
Ответ написан
Комментировать
mayton2019
@mayton2019
Bigdata Engineer
Это так не работает. Тут есть квантовый эффект что если ты подглядываешь за кодом
то он ведет себя так. А если уберешь эту формулу расчета длины то код будет собран
совсем другой.

int f(int a, int b) {
    return a + b;
}
void _end(void) {};


Может быть будет инлайнинг функции f. Поэтому расчет длины кода тебе практически
не несет никакой информации. Тоесть никаких выводов из его длины сделать нельзя.

И трюки которые работали в ассемблере могут не работать для языков где есть агрессивная
оптимизация.
Ответ написан
Комментировать
CityCat4
@CityCat4 Куратор тега C
//COPY01 EXEC PGM=IEBGENER
реализовать надо так, чтобы работало на ЛЮБОМ компиляторе

...Ответ Надсистемы мгновенно разрушил радужные надежды: «Задача не имеет решения»... (С) Л. Резник "Магический треугольник"

Задача не имеет решения даже при использовании одного и того же gcc. gcc -О2 даст тебе совершенно другие результаты нежели gcc -O0

Например gcc -O2 запросто вообще ничего не сделает, увидев
void _end(void) {};

Такие вещи обычно на асме пишут.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы