如何将调用的lib里的函数放到指定地址

本文介绍如何在KeilC环境中为特定函数分配内存地址,包括.lib文件的使用、.M51文件的生成及解读、BL51选项卡下的地址设置等步骤,并解释了与main函数中子函数地址分配的区别。

1.  在工程中添加所需的.lib文件,然后运行编译,生成.M51文件:

 

2. 打开.M51文件,找到使用函数的segment name:

 

3. 根据segment name,在KeilC 的BL51选项卡下按照自己的需求进行指定地址设置,指定地址指令如下:

 

 

设置完成后,rebulid工程,在新生成的.M51文件中确认是否按要求更改函数地址:

 

 

注意事项:

与直接分配main函数中的子函数到指定地址的操作不同处在于,BL51中的指令部分:

是:         ?PR?ADD1?CHAR(0X3900)

而不是:  ?PR?ADD1?MAIN(0X3900)

因为add1()函数是char.obj中的函数(char.obj是lib中的一个obj),不是main.obj中的函数!  

 

KEIL C将程序编译的过程如下:

1. 通过EDIT编写.c文件或汇编文件;

2. 经过compiler(C51或A51)将.c文件或汇编文件生成.obj文件。此时的.obj文件是relocatable;

3. 此时可以将.obj文件生成.lib文件,供以后的main函数.c文件调用;

第三步指的是1、2步中的.c文件不带main函数,即前两步中的文件是用来作lib程序的。如果前两步的.c文件是主函数,第三步可忽略。

4. 通过Linker工具(BL51),将第2步生成的.obj文件生成有固定地址的obj文件;

5. 通过编译器将第4步生成的obj module转换成需要的.hex文件或debuger文件或仿真文件。

 

BL51 Introduction

The BL51 Linker/Locator creates an absolute object module by linking together object modules created using the Keil A51 Assembler, C51 Compiler, Intel ASM-51 Assembler, and Intel PL/M-51 Compiler. Object modules created by these tools are relocatable and cannot be directly executed (even if they consist of only one source module). They must be linked and converted into an absolute object modules using the linker.

The following figure shows how C51 object modules flow through the Keil tool chain.

The object module generated by the BL51 Linker is an absolute object module. All relocatable sections are assigned and located at fixed addresses.

The object module includes information required for initializing global variables, zero-initializing global variables, program code and constants, as well as symbolic information, line number information, and other debugging details.

The object module may be used by the OH51 Object-HEX Converter to create an Intel HEX file for programming into ROM. It may be loaded directly into an emulator or the Keil µVision Simulator/Debugger for testing and debugging.

Note

  • By default, the object file generated by the linker has no file extension.

 

 

reference: http://www.keil.com/support/man/docs/bl51/bl51_intro.htm

                 http://www.keil.com/support/man/docs/bl51/bl51_ln_objfile.htm

<think>我们正在讨论C++中通过库调用外部程序函数的方法。根据引用内容,我们可以总结出两种主要方法:静态调用和动态调用。但引用[5]提到,日常开发中通常配置好项目属性后系统会自动加载引用的dll,无需手动加载。然而,用户的问题更侧重于“调用其他程序的函数”,而不仅仅是动态库中的函数。引用[2]提到了使用_execl函数来导入并执行新的子进程,这属于创建新进程来执行外部程序,而不是直接调用外部程序的函数。用户的问题:“我想了解如何在C++中通过库调用其他程序的函数”。这需要澄清:1.如果“外部程序”指的是另一个可执行文件(.exe),那么通常通过创建进程(如使用_execl,system等)来执行整个程序,而不是直接调用其内部的函数。因为不同进程的地址空间是隔离的。2.如果“外部程序”指的是动态链接库(DLL)中的函数,那么可以通过动态加载DLL并获取函数指针来调用。根据引用[1][3][4][5],它们讨论的都是动态库(DLL)的情况。引用[1]展示了在动态库中调用外部函数(该外部函数在宿主程序中实现),引用[4]则通过函数指针和默认实现来解决动态库调用宿主程序函数的问题。因此,我们分两种情况讨论:情况一:调用动态库(DLL)中的函数-静态调用(隐式链接):在编译时指定.lib文件,运行时dll必须可用。程序启动时系统自动加载dll。-动态调用(显式链接):使用LoadLibrary和GetProcAddress等API在运行时加载dll并获取函数指针。情况二:调用其他可执行程序(.exe)中的函数-这通常不能直接调用,因为可执行程序是独立的进程。但可以通过进程间通信(IPC)或远程过程调用(RPC)来实现。另外,也可以将可执行程序中的代码重构为动态库,然后通过情况一的方法调用。考虑到用户的问题描述,以及引用中提供的信息,我们重点讨论动态库的调用。步骤1:创建动态库(DLL)-在DLL中导出函数,可以使用__declspec(dllexport)或.def文件(引用[3]提到标准C接口和extern"C"避免名称改编)。步骤2:在应用程序中调用DLL函数-静态调用:包含头文件,链接.lib,然后像调用普通函数一样调用。-动态调用:使用LoadLibrary加载DLL,然后使用GetProcAddress获取函数地址,通过函数指针调用。另外,引用[1]和[4]还提到动态库调用宿主程序(主程序)中的函数。这需要主程序将函数注册给动态库,通常通过设置回调函数指针实现。下面我们详细说明:一、静态调用DLL函数(隐式链接)1.创建DLL时,在头文件中声明导出函数,例如://mydll.h#ifdefMYDLL_EXPORTS#defineMYDLL_API__declspec(dllexport)#else#defineMYDLL_API__declspec(dllimport)#endifextern"C"MYDLL_APIintadd(inta,intb);//使用extern"C"避免C++名称改编2.在DLL项目中定义函数,并编译生成.dll和.lib文件。3.在应用程序中包含头文件,链接.lib文件,然后直接调用add函数。二、动态调用DLL函数(显式链接)1.使用LoadLibrary加载DLL。2.使用GetProcAddress获取函数地址。3.将函数指针转换为合适的类型并调用。4.使用FreeLibrary卸载DLL。示例代码(动态调用):```cpp#include<windows.h>#include<iostream>typedefint(*AddFunc)(int,int);intmain(){HINSTANCEhDll=LoadLibrary(TEXT("mydll.dll"));if(hDll==NULL){std::cerr<<"FailedtoloadDLL."<<std::endl;return1;}AddFuncadd=(AddFunc)GetProcAddress(hDll,"add");if(add==NULL){std::cerr<<"Failedtogetfunctionaddress."<<std::endl;FreeLibrary(hDll);return1;}intresult=add(3,5);std::cout<<"3+5="<<result<<std::endl;FreeLibrary(hDll);return0;}```三、动态库调用宿主程序(主程序)的函数(回调函数)1.主程序定义函数,例如:voidmyCallback(){std::cout<<"Callbackinmainprogram."<<std::endl;}2.将函数指针传递给动态库(通过动态库提供的注册函数)。3.动态库保存函数指针,并在需要时调用。示例(动态库中提供注册函数):在DLL中:```cpp//定义函数指针类型typedefvoid(*CallbackFunc)();//全局变量保存回调函数指针CallbackFuncg_callback=nullptr;//导出函数:注册回调extern"C"MYDLL_APIvoidregisterCallback(CallbackFuncfunc){g_callback=func;}//导出函数:触发回调extern"C"MYDLL_APIvoiddoSomething(){if(g_callback){g_callback();}}```在主程序中:```cpp#include<windows.h>#include<iostream>#include"mydll.h"voidmyCallback(){std::cout<<"Callbackcalledinmain."<<std::endl;}intmain(){//加载DLL(这假设静态调用,所以直接调用registerCallback)registerCallback(myCallback);doSomething();//会调用myCallbackreturn0;}```注意:这种方式要求主程序在调用动态库函数之前先注册回调函数。关于调用其他可执行程序(.exe)中的函数,如果确实需要直接调用其内部函数,那么需要将那个可执行程序改为动态库,或者使用进程间通信(如共享内存、管道、RPC等)来间接调用。因此,总结回答用户问题:在C++中,通过库调用外部程序函数的方法主要有两种:对于动态库(DLL)中的函数,可以使用静态调用(隐式链接)或动态调用(显式链接)的方式;而对于其他可执行程序(.exe)中的函数,则不能直接调用,需要将其重构为动态库或使用进程间通信技术。根据引用[2],如果用户指的是启动另一个可执行程序,则可以使用_execl或system等函数,但这只是执行整个程序,而不是调用其内部的某个函数。下面生成相关问题:</think>### C++ 使用库调用外部程序函数的方法 在 C++ 中通过库调用外部程序函数主要有两种方式:**静态调用**(隐式链接)和**动态调用**(显式链接)。以下是详细实现方法: --- #### 一、静态调用(隐式链接) 适用于开发时已知目标 DLL 的情况。系统在程序启动时自动加载 DLL。 1. **准备头文件和导入库** ```cpp // 头文件 external_lib.h #pragma once #ifdef IMPORT_DLL #define API __declspec(dllimport) #else #define API __declspec(dllexport) #endif extern "C" API int external_func(int param); // 使用 extern "C" 避免名称改编[^3] ``` 2. **主程序调用** ```cpp #include "external_lib.h" #pragma comment(lib, "external_lib.lib") // 链接导入库 int main() { int result = external_func(42); // 直接调用外部函数 return 0; } ``` **关键配置**: - 将 DLL 和 `.lib` 文件放入可执行文件目录 - 在 IDE 中配置附加库目录(VC++ 目录 → 库目录)[^5] --- #### 二、动态调用(显式链接) 适用于运行时动态加载 DLL,更灵活但代码更复杂。 ```cpp #include <Windows.h> typedef int (*ExternalFuncPtr)(int); // 函数指针类型定义 int main() { HINSTANCE hDll = LoadLibrary(TEXT("external_lib.dll")); if (!hDll) { // 处理加载失败 return -1; } // 获取函数地址 ExternalFuncPtr func = (ExternalFuncPtr)GetProcAddress(hDll, "external_func"); if (!func) { // 处理函数获取失败 FreeLibrary(hDll); return -1; } // 调用外部函数 int result = func(42); FreeLibrary(hDll); // 卸载 DLL return 0; } ``` --- #### 三、动态库调用主程序函数(回调机制) 当 DLL 需要调用主程序中的函数时,需通过**函数指针注册**实现[^1][^4]: 1. **主程序定义函数** ```cpp // main.cpp void __cdecl main_program_func() { std::cout << "主程序函数调用\n"; } ``` 2. **DLL 提供注册接口** ```cpp // dll_lib.h typedef void (__cdecl *CallbackFunc)(); extern "C" __declspec(dllexport) void register_callback(CallbackFunc func); ``` ```cpp // dll_lib.cpp CallbackFunc g_callback = nullptr; void register_callback(CallbackFunc func) { g_callback = func; // 保存函数指针 } void internal_func() { if (g_callback) g_callback(); // 调用主程序函数 } ``` 3. **主程序注册函数** ```cpp HINSTANCE hDll = LoadLibrary("dll_lib.dll"); auto reg = (void(*)(CallbackFunc))GetProcAddress(hDll, "register_callback"); reg(main_program_func); // 注册回调 ``` --- #### 关键注意事项 1. **调用约定匹配** 确保双方使用相同调用约定(如 `__cdecl` 或 `__stdcall`)[^3]。 2. **名称改编处理** 使用 `extern "C"` 禁止 C++ 名称改编,确保 C 语言兼容性[^3]。 3. **内存管理边界** 跨模块内存分配/释放应在同一模块内完成(DLL 分配的内存在 DLL 释放)。 4. **错误处理** 动态加载时需检查: ```cpp if (!hDll) // 检查 DLL 加载 if (!func) // 检查函数获取 ``` --- ### 相关问题 1. 如何在 C++ 中处理跨 DLL 边界的内存分配和释放? 2. 使用 `extern "C"` 时如何兼容 C++ 类成员函数的导出? 3. 动态加载 DLL 时有哪些安全风险?如何防范? 4. 如何实现跨进程的函数调用(如 RPC 替代方案)? 5. 在 Linux 系统中如何实现类似的动态库调用机制? [^1]: 动态库中声明并调用外部函数 [^2]: 使用 `_execl` 执行外部程序 [^3]: 使用 `extern "C"` 解决名称改编问题 [^4]: 通过函数指针实现回调机制 [^5]: 隐式链接的配置方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值