Pergunta

Eu quero substituir certas chamadas de função para várias APIs para o bem de registrar as chamadas, mas eu também pode querer manipular os dados antes de ser enviado para a função real.

Por exemplo, digamos que eu uso uma função chamada getObjectName milhares de vezes no meu código fonte. Eu quero substituir temporariamente esta função, por vezes, porque eu quero mudar o comportamento desta função para ver o resultado diferente.

Eu criar um novo arquivo de origem como esta:

#include <apiheader.h>    

const char *getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return "name should be here";
}

eu compilar toda a minha outra fonte como eu normalmente faria, mas eu vinculá-lo contra esta função antes de ligar com a biblioteca da API. Esta multa civil excepto eu, obviamente, não pode chamar a função real dentro da minha função primordial.

Existe uma maneira mais fácil "substituir" uma função sem ficar ligando / compilação de erros / avisos? Idealmente, eu quero ser capaz de substituir a função por apenas compilar e vincular um arquivo extra ou dois em vez de em torno de mexer com ligando opções ou alterar o código fonte real do meu programa.

Foi útil?

Solução

Se for apenas para a sua fonte que deseja capturar / modificar as chamadas, a solução mais simples é montar um arquivo de cabeçalho (intercept.h) com:

#ifdef INTERCEPT
    #define getObjectName(x) myGetObectName(x)
#endif

e implementar a função da seguinte forma (em intercept.c que não incluem intercept.h):

const char *myGetObjectName (object *anObject) {
    if (anObject == NULL)
        return "(null)";
    else
        return getObjectName(anObject);
}

Em seguida, certifique-se cada arquivo de origem onde você quer interceptar a chamada tem:

#include "intercept.h"

no topo.

Então, quando você compilar com "-DINTERCEPT", todos os arquivos irá chamar sua função ao invés de uma real e sua função ainda pode chamar a um real.

Compilando sem o "-DINTERCEPT" vai evitar a intercepção de ocorrência.

É um pouco mais complicado se você quiser interceptar todas as chamadas (e não apenas os da sua fonte) - isto pode geralmente ser feito com a carga e resolução da função real (com chamadas dlload- e dlsym-type) dinâmica, mas eu não acho que é necessário no seu caso.

Outras dicas

Com gcc, no Linux você pode usar o sinalizador --wrap vinculador como esta:

gcc program.c -Wl,-wrap,getObjectName -o program

e definir a sua função como:

const char *__wrap_getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return __real_getObjectName( anObject ); // call the real function
}

Isto irá assegurar que todas as chamadas para getObjectName() são reencaminhadas para a sua função de wrapper (em tempo de link). Esta bandeira é muito útil no entanto ausente no gcc sob Mac OS X.

Lembre-se de declarar a função de invólucro com extern "C" se você está compilando com g ++ embora.

Você pode substituir uma função usando truque LD_PRELOAD - ver man ld.so. Você compilar lib compartilhada com a sua função e iniciar o binário (você mesmo não precisa modificar o binário!) Como LD_PRELOAD=mylib.so myprog.

No corpo da sua função (em lib compartilhada) que você escreve assim:

const char *getObjectName (object *anObject) {
  static char * (*func)();

  if(!func)
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName");
  printf("Overridden!\n");     
  return(func(anObject));    // call original function
}

Você pode substituir qualquer função de biblioteca compartilhada, mesmo a partir de stdlib, sem modificar / recompilar o programa, para que você possa fazer o truque em programas que você não tem uma fonte para. não é bom?

Se você usar GCC, você pode fazer a sua função weak. Aqueles pode ser substituído por funções não fracos:

test.c :

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}

O que ele faz?

$ gcc test.c
$ ./a.out
not overridden!

test1.c :

#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}

O que ele faz?

$ gcc test1.c test.c
$ ./a.out
overridden!

Infelizmente, isso não vai funcionar para outros compiladores. Mas você pode ter as declarações fracos que contêm funções substituíveis em seu próprio arquivo, colocando apenas um include para os arquivos de implementação API se você está compilando usando GCC:

weakdecls.h :

__attribute__((weak)) void test(void);
... other weak function declarations ...

functions.c :

/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...

desvantagem desta situação é que ele não funciona inteiramente sem fazer algo para os arquivos API (que necessitam essas três linhas e os weakdecls). Mas uma vez que você fez essa mudança, as funções podem ser substituídas facilmente por escrito uma definição global em um arquivo e vinculação que em.

Muitas vezes é desejável para modificar o comportamento de bases de código existentes por embrulho ou substituir as funções. Quando editar o código-fonte dos funções é uma opção viável, isso pode ser um processo simples. Quando a fonte das funções não podem ser editados (por exemplo, se as funções são fornecida pela biblioteca sistema C), em seguida, são técnicas alternativas requeridos. Aqui, apresentamos tal técnicas para UNIX, Windows e plataformas Macintosh OS X.

Esta é uma grande PDF cobrindo como isso foi feito no OS X, Linux e Windows.

Ele não tem nenhum truques incríveis que não foram documentadas aqui (este é um conjunto incrível de respostas BTW) ... mas é uma leitura agradável.

Interceptar funções arbitrárias no Windows, UNIX e Macintosh OS plataformas X (2004), de Daniel S. Myers e Adam L. Bazinet .

Você pode baixar o PDF diretamente de um local alternativo (para redundância) .

E, finalmente, se as duas fontes anteriores de alguma forma ir para baixo em chamas, aqui é um resultado de pesquisa do Google para ele .

Você pode definir um ponteiro de função como uma variável global. A sintaxe chamadores não mudaria. Quando o programa é iniciado, ele poderia verificar se alguma variável flag de linha de comando ou de ambiente é definida para ativar o registro, em seguida, salvar o valor original do ponteiro de função e substituí-lo com a sua função de registro. Você não precisa de um especial "log habilitado" de construção. Os usuários podem ativar o registro "no campo".

Você precisa ser capaz de modificar o código fonte dos chamadores, mas não o receptor (assim que este iria funcionar ao chamar bibliotecas de terceiros).

foo.h:

typedef const char* (*GetObjectNameFuncPtr)(object *anObject);
extern GetObjectNameFuncPtr GetObjectName;

foo.cpp:

const char* GetObjectName_real(object *anObject)
{
    return "object name";
}

const char* GetObjectName_logging(object *anObject)
{
    if (anObject == null)
        return "(null)";
    else
        return GetObjectName_real(anObject);
}

GetObjectNameFuncPtr GetObjectName = GetObjectName_real;

void main()
{
    GetObjectName(NULL); // calls GetObjectName_real();

    if (isLoggingEnabled)
        GetObjectName = GetObjectName_logging;

    GetObjectName(NULL); // calls GetObjectName_logging();
}

Há também um método complicado de fazê-lo no vinculador envolvendo duas bibliotecas stub.

Biblioteca # 1 está ligada com a biblioteca de acolhimento e expõe o símbolo sendo redefinido com outro nome.

Biblioteca # 2 está ligado contra biblioteca # 1, interecepting a chamada e chamar a versão redefinido na biblioteca # 1.

Tenha muito cuidado com as ordens de link aqui ou não vai funcionar.

resposta Com base @Johannes de Schaub com uma solução adequada para o código que você não possui.

Alias ??a função que deseja para substituir a função fracamente definido, e depois reimplementar-lo sozinho.

override.h

#define foo(x) __attribute__((weak))foo(x)

foo.c

function foo() { return 1234; }

override.c

function foo() { return 5678; }

Use valores de variáveis ??específicas de padrão em seu Makefile para adicionar o -include override.h bandeira compilador.

%foo.o: ALL_CFLAGS += -include override.h

Além:. Talvez você também pode usar -D 'foo(x) __attribute__((weak))foo(x)' para definir suas macros

Compilar e vincular o arquivo com o seu reimplementação (override.c).

  • Isso permite que você substituir uma única função a partir de qualquer arquivo de origem, sem ter que modificar o código.

  • A desvantagem é que você deve usar um arquivo de cabeçalho separado para cada arquivo que você deseja substituir.

Você pode usar uma biblioteca compartilhada (Unix) ou uma DLL (Windows) para fazer isso também (seria um pouco de uma penalidade de desempenho). Você pode então alterar a DLL / so que é carregado (uma versão para depuração, uma versão para a não-debug).

Eu fiz uma coisa semelhante no passado (para não conseguir o que você está tentando alcançar, mas a premissa básica é a mesma) e funcionou bem.

[Edit com base no comentário OP]

Na verdade uma das razões que eu quero funções de substituição é porque eu suspeito que eles se comportam de maneira diferente em sistemas diferentes de operação.

Existem duas maneiras comuns (que eu saiba) de lidar com isso, o lib compartilhada / dll forma ou de escrita diferentes implementações que apontam contra.

Para ambas as soluções (compartilhado libs ou ligando diferente) você teria foo_linux.c, foo_osx.c, foo_win32.c (ou a melhor maneira é linux / foo.c, OSX / foo.c e Win32 / foo.c ) e, em seguida, compilar e vincular com o apropriado.

Se você estiver olhando para ambos código diferente para diferentes plataformas e de depuração versus liberar eu provavelmente estaria inclinado a ir com o lib compartilhada / DLL solução, uma vez que é o mais flexível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top