38 如何根据地址获取函数名

ELF文件解析与函数寻址
本文介绍如何从ELF文件中解析符号表来实现根据函数地址查找函数名称的功能。详细阐述了ELF文件结构及其组成部分,如ELF头、程序头表、节头表和节等内容。

38.1 前言

之前有做过一个通过截取内核信号,获取程序出错所在函数位置信息(如段错误),然后进行栈回溯的功能(之前的blog有写),那个虽然成功了,但仍有一些不合人意的地方。就是手动回溯结果显示的只是函数地址,如果要看是哪个函数,那还要用objdump或addrline工具用地址找到是哪个函数,比较麻烦。最近折腾了两天时间,终于搞定了根据地址自动获取函数名称的功能。

不管gdb还readelf或addrline工具,可以简单轻松的敲一下命令就可以把函数名及地址整齐地打印展现出来,那么它们是怎么实现的呢??这就是本blog将要讲述的。

38.2 ELF文件及数据结构

首先了解EFL可执行文件,因为我们要找的信息就在ELF文件的符号表中。ELF文件,其全称为Executable and Linking Format,中文语义就是可执行链接格式文件,属于一种文件的存储格式。其有四个部分构成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table)。如下图示。

相信c/c++软件工程师对程序的数据组织结构有一定的了解,如一个程序有.text段、.bss段、.data段等等,但这些只是属于程序文件中的一部分。一个可执行文件真实的数据组织远不止上述几个,其可能包含二十几个信息区甚至更多,如下图示,在一个elf可执行文件中,其包含了28个section,分别存放着不同的数据。

.bss

未初始化的全局或局部变量

.comment

可执行文件的版本及控制描述信息等

.dynamic

程序动态链接信息及相关属性

.hash

符号hash表

.shstrtab

保存所有节的名称信息

.strtab

保存程序中所有的字符串信息包括函数名

.symtab

符号表

刚才讲到了,ELF头(ELF header)、程序头表(Program header table)、节头表(Section header table)和节(Section),下面就来看一下它们的数据结构是怎样的。

数据类型定义:

/* Type for a 16-bit quantity.  */  
typedef uint16_t Elf32_Half;  
 
/* Types for signed and unsigned 32-bit quantities.  */  
typedef uint32_t Elf32_Word;  
typedef int32_t  Elf32_Sword;  
    
/* Type of addresses.  */  
typedef uint32_t Elf32_Addr;  
  
/* Type of file offsets.  */  
typedef uint32_t Elf32_Off;  
  
/* Type for section indices, which are 16-bit quantities.  */  
typedef uint16_t Elf32_Section;  
  
/* Type for version symbol information.  */  
typedef Elf32_Half Elf32_Versym;  

ELF Header结构(52字节):

#define EI_NIDENT 16  
typedef struct {  
unsigned char e_ident[EI_NIDENT]; //Magic  
Elf32_Half e_type; //文件类型 2-可执行文件  
Elf32_Half e_machine; //机器类型 如arm  
Elf32_Word e_version; //文件版本  
Elf32_Addr e_entry; //程序入口地址  
Elf32_Off e_phoff; //程序头table偏移字节大小  
Elf32_Off e_shoff; //section偏移字节大小  
Elf32_Word e_flags; //processor-specific flags  
Elf32_Half e_ehsize; //elf文件头大小  
Elf32_Half e_phentsize; //程序头大小  
Elf32_Half e_phnum; //程序头数量  
Elf32_Half e_shentsize; //section头大小  
Elf32_Half e_shnum; //section数量  
Elf32_Half e_shstrndx; //字符串表索引节头位置  
} Elf32_Ehdr;  

Program Header结构(32字节):

typedef struct {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;

Section Header结构(40字节):

typedef struct {
Elf32_Word sh_name; //section名称索引(含义是在string table总的第几个字节数)
Elf32_Word sh_type; //section类型
Elf32_Word sh_flags; //
Elf32_Addr sh_addr; //section的地址
Elf32_Off sh_offset; //section偏移地址
Elf32_Word sh_size; //大小
Elf32_Word sh_link;
Elf32_Word sh_info; //节头信息
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;

Section中的Symbol Table Entry结构(16字节):

typedef struct {
Elf32_Word st_name; //索引值,值为字符串符号表的偏移字节大小(如果是函数那就是函数名)
Elf32_Addr st_value; //符号地址,如果是函数那就是函数地址
Elf32_Word st_size; //
unsigned char st_info; //符号相关信息,如是否为函数、全局类型等等
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;

//注:info转化方式如下。
#define ELF32_ST_BIND(i) ((info)>>4)
#define ELF32_ST_TYPE(i) ((info)&0xf)
#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf))

38.3 根据地址寻找函数名称思路

对于一个elf文件,其文件内容及组织形式对于不同的文件可能不一样,但一样的是elf文件的文件头对于一种架构来说是一样的,因此若获取elf文件的符号信息等,需要从elf的文件头开始。

 

(1)据上图所示,节头表的起始地址为5388字节开始(即0x150C);

(2)节头大小40字节(即0x28),节头数量29,节头共大小为29*40=1160(即0x488);

(3)节头表数据位置为:0x150C – 0x1994(0x150C+0x488);

(4)由上表可知字符串表索引头:26,因此可以直接在29个节头中定位到字符串索引表头,并获取其中内容;

 

为什么在ELF头中将字符串索引节点给出,那是因为在节头数据结构中,其节section的名字并不是字符串,而只是一个索引值,所有的节名称统一存放在.shstrtab中,因此如果想知道这个section的名称的话,那就需要去找。

        (5)根据名称匹配,可以知道那个section是.symtab符号表,然后拿到其起始地址和大小;

(6)拿到.symtab表起始地址和大小后,以Symbol Table Entry结构大小遍历全部数据。当查找的函数地址与Symbol Table Entry的value匹配时,此时找到对应函数的入口信息点,然后再根据Symbol Table Entry 的name索引值,到.strtab段找到函数名称,此时完成地址与函数名称的查找。

总结流程: 

 

 

<think>首先,用户的问题是关于在SAP SMARTFORMS中获取供应商地址的方法。用户说:“我想在SMARTFORMS中获取供应商地址 请问SMARTFORMS 获取供应商地址的方法”。 根据系统级指令: - 所有行内数学表达式使用$...$格式,但这里没有数学内容,所以不需要。 - 独立公式使用$$...$$,但同样不适用。 - LaTeX语法正确:不涉及数学。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 用户引用了几个内容: - 引用[1]: 关于SMARTFORM转PDF的设置。 - 引用[2]: 一个ABAP报告代码,包括INCLUDE文件。 - 引用[3]: 一个动态表行转列的开发实例。 这些引用似乎是用户提供的上下文,但它们不是直接关于供应商地址的。用户说“上述引用仅供参考”,所以我需要基于用户的问题来回答。 用户的核心问题是:在SMARTFORMS中获取供应商地址的方法。 在SAP SMARTFORMS中,获取供应商地址通常涉及从数据库表中检索数据。供应商地址信息通常存储在表如LFA1(供应商主数据)或ADRC(地址数据)中。 步骤: 1. **理解需求**:供应商地址可能包括名称、街道、城市、邮编等。 2. **数据源**:主要表是LFA1(供应商主),但地址细节可能在ADRC(地址管理)中。LFA1有字段ADRNR(地址编号),指向ADRC表。 3. **在SMARTFORMS中获取**: - SMARTFORMS通常与ABAP程序结合使用。ABAP程序负责获取数据,然后传递给SMARTFORMS。 - 在ABAP程序中,你需要编写代码来从LFA1和ADRC表中读取数据。 - 然后,在SMARTFORMS中定义变量或使用全局数据来显示这些地址。 具体方法: - 在ABAP报告中: - 声明变量来存储地址信息。 - 使用SELECT语句从LFA1和ADRC表中获取数据。 - 例如:SELECT SINGLE * FROM LFA1 WHERE LIFNR = vendor_number. 然后从ADRC获取地址。 - 在SMARTFORMS中: - 在表格接口中定义变量。 - 在输出文本中,使用&变量名&来显示值。 用户提供的引用中: - 引用[2]有一个ABAP报告ZSF_ZSMART_F002,它包括了INCLUDE文件,可能涉及数据处理。 - 引用[3]提到动态表行转列,但可能不直接相关。 所以,我应该基于标准SAP知识来回答。 回答结构: 1. 解释基本方法。 2. 提供步骤或示例代码。 3. 生成相关问题。 由于用户要求生成相关问题,我需要添加在最后。 确保回答真实可靠:基于SAP标准功能。 最终回答用中文。</think>### SMARTFORMS 中获取供应商地址的方法 在 SAP SMARTFORMS 中获取供应商地址,通常需要结合 ABAP 程序从数据库表中检索数据,并通过 SMARTFORMS 的表格接口传递数据。供应商地址信息主要存储在标准表 `LFA1`(供应商主数据)和 `ADRC`(地址数据)中。`LFA1.ADRNR` 字段指向 `ADRC.ADDRNUMBER`,用于关联地址细节。以下是详细步骤和示例,帮助您逐步实现。 #### 步骤 1: 在 ABAP 程序获取供应商地址数据 首先,在调用 SMARTFORMS 的 ABAP 报告中编写代码,从表中读取供应商地址。关键点: - 使用 `SELECT` 语句从 `LFA1` 和 `ADRC` 表中检索数据。 - 将地址数据存储在结构或变量中,传递给 SMARTFORMS。 - 示例代码(基于用户提供的引用[2]扩展): ```abap REPORT ZSF_ZSMART_F002. " 假设这是主程序,类似引用[2]中的结构 INCLUDE ZSF_ZSMART_F002_TOP. " 定义全局变量 INCLUDE ZSF_ZSMART_F002_SCREEN. " 屏幕定义(可选) INCLUDE ZSF_ZSMART_F002_F01. " 子例程 INCLUDE ZSF_ZSMART_F002_O01. " PBO 模块 DATA: lv_lifnr TYPE LFA1-LIFNR, " 供应商编号 ls_lfa1 TYPE LFA1, " LFA1 结构 ls_adrc TYPE ADRC. " ADRC 结构 START-OF-SELECTION. PERFORM FRM_GET_DATA. " 调用数据获取子例程 FORM FRM_GET_DATA. " 假设供应商编号从选择屏幕或参数传入 lv_lifnr = '100000'. " 示例供应商编号 " 从 LFA1 表获取供应商主数据 SELECT SINGLE * FROM LFA1 INTO ls_lfa1 WHERE LIFNR = lv_lifnr. " 从 ADRC 表获取地址数据(使用 LFA1 中的地址编号) IF ls_lfa1-ADRNR IS NOT INITIAL. SELECT SINGLE * FROM ADRC INTO ls_adrc WHERE ADDRNUMBER = ls_lfa1-ADRNR. ENDIF. " 将地址数据传递给 SMARTFORMS(通过全局变量或结构) " 注意:实际需在 SMARTFORMS 表格接口中定义对应字段 ENDFORM. ``` #### 步骤 2: 在 SMARTFORMS 中配置表格接口和输出 1. **定义表格接口**: - 打开 SMARTFORMS 事务码(SMARTFORMS 或 SMARTFORMS)。 - 创建或修改表单,转到“表格接口”标签页。 - 添加参数(例如 `IM_ADRC` 类型 `ADRC`),用于接收 ABAP 程序传递的地址数据。 - 确保参数与 ABAP 中的结构匹配(如 `ls_adrc`)。 2. **在输出元素中显示地址**: - 在 SMARTFORMS 的“页和窗口”中,添加文本元素。 - 使用 `&` 符号引用接口参数,例如: - 供应商名称: `&IM_ADRC-NAME1&` - 街道: `&IM_ADRC-STREET&` - 城市: `&IM_ADRC-CITY1&` - 邮编: `&IM_ADRC-POST_CODE1&` - 确保文本元素使用正确的格式(如段落或表格)。 3. **调用 SMARTFORMS**: - 在 ABAP 报告中,使用 `CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME'` 获取表单函数模块。 - 传递地址数据到表单接口。 - 示例代码(添加到 `FRM_SHOW_DATA` 或类似位置): ```abap FORM FRM_SHOW_DATA. DATA: lv_fm_name TYPE RS38L_FNAM, ls_output TYPE SSFCOMPOP. " 输出控制参数 " 获取 SMARTFORMS 函数模块名 CALL FUNCTION 'SSF_FUNCTION_MODULE_NAME' EXPORTING FORMNAME = 'ZSF_VENDOR_ADDRESS' " 您的 SMARTFORMS 名称 IMPORTING FM_NAME = lv_fm_name EXCEPTIONS NO_FORM = 1 NO_FUNCTION_MODULE = 2 OTHERS = 3. " 调用 SMARTFORMS 并传递地址数据 CALL FUNCTION lv_fm_name EXPORTING CONTROL_PARAMETERS = ls_output IM_ADRC = ls_adrc " 传递 ADRC 结构 EXCEPTIONS FORMATTING_ERROR = 1 INTERNAL_ERROR = 2 OTHERS = 3. ENDFORM. ``` #### 关键注意事项 - **数据关联**:确保供应商编号(`LIFNR`)在 ABAP 程序中正确传入(例如从选择屏幕、采购订单表如 `EKKO`/`EKPO` 获取)。 - **错误处理**:在 `SELECT` 语句中添加异常处理(如 `SY-SUBRC` 检查),避免空数据导致表单错误。 - **性能优化**:使用 `SELECT SINGLE` 避免全表扫描,减少数据库负载。 - **参考设置**:如果表单需要 PDF 输出,确保在调用函数时设置 `CONTROL_PARAMETERS-NO_DIALOG = 'X'` 和 `CONTROL_PARAMETERS-GETOTF = 'X'`,以便正确转换(如引用[1]所述)[^1]。 - **测试**:使用事务码 SMARTFORMS 预览表单,或结合 ABAP 报告运行测试(参考引用[2]中的测试流程)[^2]。 #### 常见问题解决 - **地址为空**:检查 `LFA1.ADRNR` 是否有效,并验证 `ADRC` 表中是否存在对应记录。 - **表单显示错误**:确保表格接口参数类型与 ABAP 结构一致(例如都定义为 `ADRC` 类型)。 - **动态数据需求**:如果地址需要动态处理(如行转列),参考引用[3]中的动态表技术,但本场景通常不需要[^3]。 通过以上步骤,您可以在 SMARTFORMS 中成功获取并显示供应商地址。如果您有特定供应商来源(如采购订单),请提供更多细节以便进一步优化。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值