动态链接 GAS 组件文件时的问题 (x64)

如何解决动态链接 GAS 组件文件时的问题 (x64)

我正在尝试链接 2 个文件。我试图链接 3 个符号(汇编程序)。它与静态链接完美配合,但在尝试动态链接时 - 我收到错误消息。

/usr/bin/ld: warning: type and size of dynamic symbol `parse_intro' are not defined                                                                                                                         
/usr/bin/ld: warning: type and size of dynamic symbol `time_to_print' are not defined                                                                                                                       
/usr/bin/ld: warning: type and size of dynamic symbol `optimizing' are not defined  

首先,我将一个文件制作成一个共享库,我想从中导出 3 个符号。该文件的代码:

    .data
premsg:
    .ascii "0x"                                     #to make the number have distinct x16 look when printed,this will be used as a prefix before a number

msg:                                                #placeholder msg label,reserved memory will be used to store parsed number
    .space 16                                       #16 bytes for 32 ascii numbers

nextline:
    .ascii "\n"                                     #switching to the next line after 16 ascii numbers

var1:
    .quad 0x00000000000ef12b                        #number which will be parsed

    .global parse_intro
    .global optimizing
    .global time_to_print

    .text

parse_intro:
                                                    #binary mask for cutting smallest 4 bits (single number) from the 8 byte number
    mov $0b0000000000000000000000000000000000000000000000000000000000001111,%r9
    mov $0x1,%rbx                                  #multiplier that will be used to shift from first 4 bits to proper position in the ascii number
    mov $0x0,%r12                                  #register that will hold first half of the reversed number
    mov $0x0,%r14                                  #register that will hold second half of the reversed number
    jmp parse_start

parse_start:                                        #main body of the parsing number to ASCII procedure
    mov %r8,%rax                                   #making a copy of a number before shifting it
    shr $4,%r8                                     #shifting original number to 4 numbers right to go to the next number on the next step
    and %r9,%rax                                   #taking last 4 bits from a number
    cmp $0xa,%rax                                  #checking if a number is smaller than 0xa
    jl zero_to_9                                    #jumping to do +30 procedure if it's not x16 numeric symbol

    add $0x57,%rax                                 #adding 57 because to change a number to ASCII number from a to f you need to add x16 57
    mul %rbx
    mul %rbx                                        #we multiplying it twice because we go from 8 bytes to 16 bytes
    add %rax,%r12                                  #adding summed with 30 and increased twice by rbx coefficient piece of the initial number to the previous pieces of a number to put it together in ASCII
    shl $4,%rbx                                    #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
    jmp check_for_overflow                          #going to overflow check after adding 8 bytes to %r12


zero_to_9:                                          #function to change from bit number to ascii number
    add $0x30,%rax                                 #adding 30 because to change a number to ASCII number from 0 to 9 you need to add x16 30
    mul %rbx
    mul %rbx                                        #we multiplying it twice because we go from 8 bytes to 16 bytes
    add %rax,%rbx                                    #shifting rbx 4 bits to the left to increase it by mul 10 without rax shenanigans
    jmp check_for_overflow                          #going to overflow check after adding 8 bytes to %r12

check_for_overflow:                                 #function to store half of the value inside a second register,because it goes from 8 bytes to 16 bytes when parsed to ASCII
    mov $0x1000000000000000,%rax                   #value for overflow check
    cmp %rax,%r12                                  #checking if r12 is overflowed
    jle looping                                     #doing a loop to parse_start with retq if loop ends in case if %r12 is not going to get overflow
    cmp $0x1,%rcx                                  #checking if count is 1 and then
    je finishing                                    #Finishing to avoid moving %r12 to %r14 another time
    mov %r12,%r14                                  #moving %r12 value to store in %r14
    xor %r12,%r12                                  #resetting %r12
    mov $0x1,%rbx                                  #resetting rbx to start from the first position
    jmp looping                                     #doing a loop to parse_start with retq if loop ends


looping:
    loop parse_start
    retq                                            #exiting back to start if loops end,otherwise back to parse_start and decrementing %rcx

optimizing:
                                                    #binary mask for cutting lower 4 bytes out in optimization routine
    mov $0b1111111111111111111111111111111100000000000000000000000000000000,%rbx

    mov $16,%rcx                                   #the counter
    movq %r8,%rax                                  #moving the initial number value to accumulator register %rax
    andq %rbx,%rax                                 #leaving only first 32 not as 0 to check if biggest part of the number is full of 0 or not
    cmp $0,%rax                                    #checking if number is full of 0
    jne finishing                                   #first (from the left) 32 bits aren't full of 0 so we can't ignore them
    subq $8,%rcx                                   #second half of a number is full of 0,so we can only count from 8 instead of 16
    retq                                            #returning to _start

finishing:
    retq                                            #little function to jump return

time_to_print:
    mov $0b1111111100000000000000000000000000000000000000000000000000000000,%r9
    cmp $0,%r14                                    #checking if we skipped 4 bytes because the original had zeroes in the biggest part of the number
    je skipped_bytes                                #jumping to function that will swap r14 and r12,so that lower part is kept in r14 and r12 is full of zeroes (0x30 in ASCII)
    mov $56,%rcx                                   #making a counter which would stop the jump loop which would also work as a shift left value
    call byte_fun                                   #calling the reversing procedure
    mov %r14,%r12                                  #moving second half of the number to r12
    mov %rax,%r14                                  #storing the first half of the number from the accumulator having the value after completing byte_fun to %r14
    mov $56,%rcx                                   #we are putting 56 and not 64 because the last step will be made after the loop to avoid additional actions
    call byte_fun                                   #reversing the second half of a number
    mov %rax,%r12                                  #moving stored second half of the number to the different register
    lea msg(%rip),%rax                             #storing the address of the msg inside %rax
    mov %r14,(%rax)                                #Putting on the first half of the reserved memory by msg,linked to %rax through previous instruction,the first half of the number needed to display formatted to ASCII saying mov value (rax) makes you move the value to the address stored in the register
    add $8,%rax                                    #adding 8 to address the second half of the reserved memory in msg
    mov %r12,(%rax)                                #putting the second half of the reversed number to an address of the last 8 bytes resrved by msg
    mov $1,%rax                                    #putting 1 to %rax for printing syscall
    mov $1,%rdi                                    #puttin 1 to %rdi for printing syscall
    lea msg(%rip),%rsi                             #taking a position independent link to the msg label with numbers related to ascii data and putting it to a printing register %rsi
    mov $16,%rdx                                   #setting length of 16 bits (2 per 1 number) and 1 bit for newline \n char
    syscall                                         #syscalling the print with the proper number
    retq                                            #returning to the _start body

byte_fun:                                           #start of the reverse function to save the entry point
    pop %rbp                                        #putting entry point to the %rbp register to successfully return after a few jumping back and forth
    jmp byte_reverse                                #jumping to the main body of the reverse function

byte_reverse:                                       #function which separates the highest 2 bits from the reversed number and then shifts the reversed number by 8 bits (2 numbers) to the left and then switches 8 bits to their proper position and pushes them to a stack.
    mov %r12,%rax                                  #putting a copy of the currently shifted reversed number to an accumulator register %rax
    shl $8,%r12                                    #shifting reversed number to the left for the next loop cycle
    and %r9,%rax                                   #applying binary mask which will only leave 8 bytes (or 2 numbers)
    shr %cl,%rax                                   #shifting current 8 bits of a reversed number right,prior to %rcx count,to reverse their position
    push %rax                                       #pushing a shifted piece of a number to a stack
    sub $8,%rcx                                    #substracting 8 from count to represent a shifting of the next 8 bits
    jne byte_reverse                                #if sub from %rcx not resulted in zero - we are looping
    push %r12                                       #pushing last 2 numbers of the reversed number,shifted to the left,as the final piece of the reversed value,which will be accessed first from stack to start the reverse
    mov $8,%rcx                                    #switching count register to 8 for a future loop inside byte_back
    xor %rax,%rax                                  #cleaning up the accumulator to 0
    jmp byte_back                                   #jumping to a code which put 8 elements inside stack all together and puts them back to stack as a single entity

byte_back:                                          #function which sums elements in the stack to get the reversed version of the number
    pop %rbx                                        #poppint highest stack element to a register
    add %rbx,%rax                                  #adding the highest element to an accumulator
    loop byte_back                                  #decrementing the %rcx counter and starting at byte back again
    push %rbp                                       #we exited the loop and now putting the address of the print procedure to the top of the stack to get back to a procedure after calling the byte_fun
    retq                                            #returning to print procedure

skipped_bytes:                                      #function to avoid issues with printing when only 4 bytes of the original number were evaluated in parsing
    #I put this print of "0x" to avoid _start code because I want to use this program as a library
    mov $1,%rax                                    #putting 1 to %rax for print syscall
    mov $1,%rdi                                    #putting 1 to %rdi for print syscall
    lea premsg(%rip),%rsi                          #putting a link to "0x" ascii value to %rsi to print it
    mov $2,%rdx                                    #setting 2 bytes to display 2 characters
    syscall                                         #syscall for printing "0x"
    mov %r12,%r14                                  #moving the first half of the reversed number to a register that will be pushed to a stack first,so it would be last when we get it back
    mov $0x3030303030303030,%r12                   #changing a second half or reversed number to ascii zeroes
    jmp time_to_print                               #we are ready to start the printing procedure

我用一个命令把这个程序变成了 .so 文件:

gcc printing.s -shared -o libprint.so

然后,我在包含此代码的主文件中使用此 .so 中的这 3 个程序(parse_intro、time_to_print 和优化)

    .data
linked_space:                                                                           #space reserved for linked list nodes
    .space 0x3000

list_head:                                                                              #default list_head value
    .quad 0x0
    .quad 0x0

opening_bracket:                                                                        #part of printing function construct
    .ascii "["

straight_line:                                                                          #part of printing function construct
    .ascii "|"

closing_part:                                                                           #part of printing function construct
    .ascii "] -> "

last_part:                                                                              #part of printing function construct,representing empty "first" node
    .ascii "[empty|node]\n"

cut_error_text:
    .ascii "Error: can't cut a core node\n"


    .global _start

    .text





add_head:                                                                               #function which adds new element as head and makes a link to a previous head element
    mov list_head(%rip),%rax                                                           #moving contents of a label (link to a head node) to a register
    add $16,%rax                                                                       #Moving the link to a point where a new node will start (1 node is 16 bytes)
    mov %rbx,(%rax)                                                                    #Putting a value that we want to hold in a new node inside the value address of a new head node
    add $8,%rax                                                                        #Moving address inside register by 8 to put a link to a previous head
    mov list_head(%rip),%rdx                                                           #Moving old head address to an %rdx to put it then inside the new head
    mov %rdx,(%rax)                                                                    #Putting link of the old head inside a node of a new head
    add $16,list_head(%rip)                                                            #Changing label which points to the head element to the new node we created
    retq

cut_head:                                                                               #Function which cuts the head by shifting the label 16 bytes back,with exception check to avoid touching core node
    mov list_head(%rip),%rax                                                           #Putting link of the current head element which we will cutaway
    add $8,%rax                                                                        #Getting a link to a previous element to check if it's 0x0,which means it's a core node
    cmp $0,(%rax)                                                                      #Comparing it to 0
    je cut_error                                                                        #Jumping to error version of the cut if it's equal
    sub $24,%rax                                                                       #Moving to the beginning of the previous element,16 bytes + 8 after previous add 8
    mov %rax,list_head(%rip)                                                           #Moving new link to a head label
    retq

cut_error:                                                                              #Function which prints error text and avoid cutting the core element
    mov $1,%rax                                                                        #Printing error text
    mov $1,%rdi
    lea cut_error_text(%rip),%rsi
    mov $29,%rdx
    syscall
    retq

pre_print:                                                                              #Intro for print to put first head element,to avoid issues with loop shenanigans
    push list_head(%rip)
    jmp print_node                                                                      #Moving to main print function

print_node:                                                                             #Function which takes functions from printing code and prints linked list visually
    pop %rbx                                                                            #Putting stored link to node value into %rbx
    push %rbx                                                                           #Pushing back to a stack a link to avoid mutations of a link after working with %rbx
    add $8,%rbx                                                                        #Moving link to the address part of the node with the address to a previous list
    mov (%rbx),%rax                                                                    #Putting actual address to the previous node to check if it's 0x0,which means,it's a first empty node
    cmp $0,%rax                                                                        #Comparing link inside %rax to 0
    je return_printing                                                                  #If it's zero - moving to the final part of the print,where the first empty node is printed with \n char
    mov $1,%rax                                                                        #Printing opening bracket
    mov $1,%rdi
    lea opening_bracket(%rip),%rsi
    mov $1,%rdx
    syscall
    pop %rbx                                                                            #Getting link to the value field of the current node
    mov (%rbx),%r8                                                                     #Moving it to %r8,which is a register that will contain a numeric value for future printing
    push %rbx                                                                           #Saving link stored inside %rbx because %rbx will be used in printing functions
    call optimizing                                                                     #Printing routine consists of 3 functions which need to be called from printing part of the program
    call parse_intro
    call time_to_print
    mov $1,%rax                                                                        #Printing straight line to separate value from link
    mov $1,%rdi
    lea straight_line(%rip),%rdx
    syscall
    pop %rbx                                                                            #Putting link back from the stack again in rbx
    push %rbx                                                                           #Storing the link before mutating it again
    add $8,%rbx                                                                        #Changing link to the address which has a link to the previous element
    mov (%rbx),%r8                                                                     #Moving link to %r8 to print it
    call optimizing                                                                     #Launching a print routine
    call parse_intro
    call time_to_print
    mov $1,%rax                                                                        #Printing closing part of the node "construct"
    mov $1,%rdi
    lea closing_part(%rip),%rsi
    mov $5,%rdx
    syscall
    pop %rbx                                                                            #Popping link of the printed node to shift it 16 bytes back to move to the previous node
    sub $16,%rbx                                                                       #Moving to the previous node
    push %rbx                                                                           #Putting a link to the previous node inside stack to use it later
    jmp print_node                                                                      #Looping back to print_node

return_printing:                                                                        #Finalizing function which will print the first empty node and \n char
    mov $1,%rax
    mov $1,%rdi
    lea last_part(%rip),%rsi
    mov $13,%rdx
    syscall
    pop %rbx                                                                            #Since we didn't pop the stored value in loop body,we need to get rid of it from stack to jump back to _start
    retq

list_initialization:
    lea linked_space(%rip),%rax                                                        #initializing the first node,it already has 0x0 as value and it's needed to put on the space for the linked list
    mov %rax,list_head(%rip)                                                           #Putting link to the list_head,which currently contains the first node value/link 0x0,on the linked_space
    retq

_start:
    call list_initialization


    #call pre_print                                                                      #Function which prints the linked list
    #mov $1,%rbx                                                                        #%rbx will hold value which will be put inside new linked list node
    #call add_head                                                                       #Function which adds new node as head element
    #call pre_print
    #mov $2,%rbx
    #call add_head
    #call pre_print
    #mov $3,%rbx
    #call add_head
    #call pre_print
    #call cut_head                                                                       #Function which cuts head element and moves link to a previous element
    #call pre_print
    #call cut_head
    #call pre_print
    #call cut_head
    #call pre_print
    #call cut_head
    #call pre_print

    mov $60,%rax                                                                       #Exiting from a program
    xor %rdi,%rdi
    syscall

并编译成二进制文件,应该可以运行,通过命令:

gcc -L /home/*path_to_folder_with_so_file* -g -nostdlib -o output linkedlist.s -lprint

我也尝试编译同一个文件,但注释掉了程序试图从动态共享库访问文件的 6 行。这是这个文件的“readelf”内容。

https://pastebin.com/WUQz09K2

以及.so文件的读取:

https://pastebin.com/1F74euqP

我可能遗漏了什么,为什么原始文件无法从 .so 中找到导入的符号?

解决方法

由于我无法发表评论作为解决方案,所以我将自己发表一个答案,这是fuz给出的。

这里要做的重要事情是注意错误消息(我知道,多么明显)。 GAS 程序集中的每个动态链接符号,即使没有 C 库,也需要在 .so 文件中分配类型和大小,因为 GCC 无法自行获取有关导出符号的这些信息,程序员需要明确给出.例如,对于函数 foo

foo:
   ...
   ret

你需要给一个函数一个类型

.type foo,@function

在代码中的某处,我在

之后的下一行做了
.global foo

我将相同函数设为全局的那一行,允许将其导出。

而且,要为动态符号指定大小,您需要将

.size foo,.-foo

在最后一条指令之后(例如,在“jmp”或“ret”之后)。 “点”是当前地址,而 foo 是“foo”函数内第一条指令的地址。因此,通过从当前地址中减去“foo”的地址,您将获得“foo”的大小。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res