1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #pragma ident   "%Z%%M% %I%     %E% SMI"
  27 
  28 #include <stdio.h>
  29 #include <stdlib.h>
  30 #include <string.h>
  31 #include <strings.h>
  32 #include <sys/types.h>
  33 #include <sys/link.h>
  34 #include <libproc.h>
  35 #include <proc_service.h>
  36 #include <rtld_db.h>
  37 #include <synch.h>
  38 
  39 #include <sys/lx_brand.h>
  40 
  41 /*
  42  * ATTENTION:
  43  *      Librtl_db brand plugin libraries should NOT directly invoke any
  44  *      libproc.so interfaces or be linked against libproc.  If a librtl_db
  45  *      brand plugin library uses libproc.so interfaces then it may break
  46  *      any other librtld_db consumers (like mdb) that tries to attach
  47  *      to a branded process.  The only safe interfaces that the a librtld_db
  48  *      brand plugin library can use to access a target process are the
  49  *      proc_service(3PROC) apis.
  50  */
  51 
  52 /*
  53  * M_DATA comes from some streams header file but is also redifined in
  54  * _rtld_db.h, so nuke the old streams definition here.
  55  */
  56 #ifdef M_DATA
  57 #undef M_DATA
  58 #endif /* M_DATA */
  59 
  60 /*
  61  * For 32-bit versions of this library, this file get's compiled once.
  62  * For 64-bit versions of this library, this file get's compiled twice,
  63  * once with _ELF64 defined and once without.  The expectation is that
  64  * the 64-bit version of the library can properly deal with both 32-bit
  65  * and 64-bit elf files, hence in the 64-bit library there are two copies
  66  * of all the interfaces in this file, one set named *32 and one named *64.
  67  *
  68  * This also means that we need to be careful when declaring local pointers
  69  * that point to objects in another processes address space, since these
  70  * pointers may not match the current processes pointer width.  Basically,
  71  * we should avoid using data types that change size between 32 and 64 bit
  72  * modes like: long, void *, uintprt_t, caddr_t, psaddr_t, size_t, etc.
  73  * Instead we should declare all pointers as uint32_t.  Then when we
  74  * are compiled to deal with 64-bit targets we'll re-define uint32_t
  75  * to be a uint64_t.
  76  *
  77  * Finally, one last importante note.  All the 64-bit elf file code
  78  * is never used and can't be tested.  This is because we don't actually
  79  * support 64-bit Linux processes yet.  The reason that we have it here
  80  * is because we want to support debugging 32-bit elf targets with the
  81  * 64-bit version of this library, so we need to have a 64-bit version
  82  * of this library.  But a 64-bit version of this library is expected
  83  * to provide debugging interfaces for both 32 and 64-bit elf targets.
  84  * So we provide the 64-bit elf target interfaces, but they will never
  85  * be invoked and are untested.  If we ever add support for 64-bit elf
  86  * Linux processes, we'll need to verify that this code works correctly
  87  * for those targets.
  88  */
  89 #ifdef _LP64
  90 #ifdef _ELF64
  91 #define lx_ldb_get_dyns32               lx_ldb_get_dyns64
  92 #define lx_ldb_init32                   lx_ldb_init64
  93 #define lx_ldb_fini32                   lx_ldb_fini64
  94 #define lx_ldb_loadobj_iter32           lx_ldb_loadobj_iter64
  95 #define lx_ldb_getauxval32              lx_ldb_getauxval64
  96 #define lx_elf_props32                  lx_elf_props64
  97 #define _rd_get_dyns32                  _rd_get_dyns64
  98 #define _rd_get_ehdr32                  _rd_get_ehdr64
  99 #define uint32_t                        uint64_t
 100 #define Elf32_Dyn                       Elf64_Dyn
 101 #define Elf32_Ehdr                      Elf64_Ehdr
 102 #define Elf32_Phdr                      Elf64_Phdr
 103 #endif /* _ELF64 */
 104 #endif /* _LP64 */
 105 
 106 /* Included from usr/src/cmd/sgs/librtld_db/common */
 107 #include <_rtld_db.h>
 108 
 109 typedef struct lx_rd {
 110         rd_agent_t              *lr_rap;
 111         struct ps_prochandle    *lr_php;        /* proc handle pointer */
 112         uint32_t                lr_rdebug;      /* address of lx r_debug */
 113         uint32_t                lr_exec;        /* base address of executable */
 114 } lx_rd_t;
 115 
 116 typedef struct lx_link_map {
 117         uint32_t lxm_addr;      /* Base address shared object is loaded at.  */
 118         uint32_t lxm_name;      /* Absolute file name object was found in.  */
 119         uint32_t lxm_ld;        /* Dynamic section of the shared object.  */
 120         uint32_t lxm_next;      /* Chain of loaded objects.  */
 121 } lx_link_map_t;
 122 
 123 typedef struct lx_r_debug {
 124         int r_version;          /* Version number for this protocol.  */
 125         uint32_t        r_map;  /* Head of the chain of loaded objects. */
 126 
 127         /*
 128          * This is the address of a function internal to the run-time linker,
 129          * that will always be called when the linker begins to map in a
 130          * library or unmap it, and again when the mapping change is complete.
 131          * The debugger can set a breakpoint at this address if it wants to
 132          * notice shared object mapping changes.
 133          */
 134         uint32_t        r_brk;
 135         r_state_e       r_state; /* defined the same way between lx/solaris */
 136         uint32_t        r_ldbase; /* Base address the linker is loaded at. */
 137 } lx_r_debug_t;
 138 
 139 static uint32_t
 140 lx_ldb_getauxval32(struct ps_prochandle *php, int type)
 141 {
 142         const auxv_t            *auxvp = NULL;
 143 
 144         if (ps_pauxv(php, &auxvp) != PS_OK)
 145                 return ((uint32_t)-1);
 146 
 147         while (auxvp->a_type != AT_NULL) {
 148                 if (auxvp->a_type == type)
 149                         return ((uint32_t)(uintptr_t)auxvp->a_un.a_ptr);
 150                 auxvp++;
 151         }
 152         return ((uint32_t)-1);
 153 }
 154 
 155 /*
 156  * A key difference between the linux linker and ours' is that the linux
 157  * linker adds the base address of segments to certain values in the
 158  * segments' ELF header. As an example, look at the address of the
 159  * DT_HASH hash table in a Solaris section - it is a relative address
 160  * which locates the start of the hash table, relative to the beginning
 161  * of the ELF file. However, when the linux linker loads a section, it
 162  * modifies the in-memory ELF image by changing address of the hash
 163  * table to be an absolute address. This is only done for libraries - not for
 164  * executables.
 165  *
 166  * Solaris tools expect the relative address to remain relative, so
 167  * here we will modify the in-memory ELF image so that it once again
 168  * contains relative addresses.
 169  *
 170  * To accomplish this, we walk through all sections in the target.
 171  * Linux sections are identified by pointing to the linux linker or libc in the
 172  * DT_NEEDED section. For all matching sections, we subtract the segment
 173  * base address to get back to relative addresses.
 174  */
 175 static rd_err_e
 176 lx_ldb_get_dyns32(rd_helper_data_t rhd,
 177     psaddr_t addr, void **dynpp, size_t *dynpp_sz)
 178 {
 179         lx_rd_t                 *lx_rd = (lx_rd_t *)rhd;
 180         rd_agent_t              *rap = lx_rd->lr_rap;
 181         Elf32_Ehdr              ehdr;
 182         Elf32_Dyn               *dynp = NULL;
 183         size_t                  dynp_sz;
 184         uint_t                  ndyns;
 185         int                     i;
 186 
 187         ps_plog("lx_ldb_get_dyns: invoked for object at 0x%p", addr);
 188 
 189         /* Read in a copy of the ehdr */
 190         if (_rd_get_ehdr32(rap, addr, &ehdr, NULL) != RD_OK) {
 191                 ps_plog("lx_ldb_get_dyns: _rd_get_ehdr() failed");
 192                 return (RD_ERR);
 193         }
 194 
 195         /* read out the PT_DYNAMIC elements for this object */
 196         if (_rd_get_dyns32(rap, addr, &dynp, &dynp_sz) != RD_OK) {
 197                 ps_plog("lx_ldb_get_dyns: _rd_get_dyns() failed");
 198                 return (RD_ERR);
 199         }
 200 
 201         /*
 202          * From here on out if we encounter an error we'll just return
 203          * success and pass back the unmolested dynamic elements that
 204          * we've already obtained.
 205          */
 206         *dynpp = dynp;
 207         *dynpp_sz = dynp_sz;
 208         ndyns = dynp_sz / sizeof (Elf32_Dyn);
 209 
 210         /* If this isn't a dynamic object, there's nothing left todo */
 211         if (ehdr.e_type != ET_DYN) {
 212                 ps_plog("lx_ldb_get_dyns: done: not a shared object");
 213                 return (RD_OK);
 214         }
 215 
 216         /*
 217          * Before we blindly start changing dynamic section addresses
 218          * we need to figure out if the current object that we're looking
 219          * at is a linux object or a solaris object.  To do this first
 220          * we need to find the string tab dynamic section element.
 221          */
 222         for (i = 0; i < ndyns; i++) {
 223                 if (dynp[i].d_tag == DT_STRTAB)
 224                         break;
 225         }
 226         if (i == ndyns) {
 227                 ps_plog("lx_ldb_get_dyns: "
 228                     "failed to find string tab in the dynamic section");
 229                 return (RD_OK);
 230         }
 231 
 232         /*
 233          * Check if the strtab value looks like an offset or an address.
 234          * It's an offset if the value is less then the base address that
 235          * the object is loaded at, or if the value is less than the offset
 236          * of the section headers in the same elf object.  This check isn't
 237          * perfect, but in practice it's good enough.
 238          */
 239         if ((dynp[i].d_un.d_ptr < addr) ||
 240             (dynp[i].d_un.d_ptr < ehdr.e_shoff)) {
 241                 ps_plog("lx_ldb_get_dyns: "
 242                     "doesn't appear to be an lx object");
 243                 return (RD_OK);
 244         }
 245 
 246         /*
 247          * This seems to be a a linux object, so we'll patch up the dynamic
 248          * section addresses
 249          */
 250         ps_plog("lx_ldb_get_dyns: "
 251             "patching up lx object dynamic section addresses");
 252         for (i = 0; i < ndyns; i++) {
 253                 switch (dynp[i].d_tag) {
 254                 case DT_PLTGOT:
 255                 case DT_HASH:
 256                 case DT_STRTAB:
 257                 case DT_SYMTAB:
 258                 case DT_RELA:
 259                 case DT_REL:
 260                 case DT_DEBUG:
 261                 case DT_JMPREL:
 262                 case DT_VERSYM:
 263                         if (dynp[i].d_un.d_val > addr) {
 264                                 dynp[i].d_un.d_ptr -= addr;
 265                         }
 266                         break;
 267                 default:
 268                         break;
 269                 }
 270         }
 271         return (RD_OK);
 272 }
 273 
 274 static void
 275 lx_ldb_fini32(rd_helper_data_t rhd)
 276 {
 277         lx_rd_t *lx_rd = (lx_rd_t *)rhd;
 278         ps_plog("lx_ldb_fini: cleaning up lx helper");
 279         free(lx_rd);
 280 }
 281 
 282 /*
 283  * The linux linker has an r_debug structure somewhere in its data section that
 284  * contains the address of the head of the link map list. To find this, we will
 285  * use the DT_DEBUG token in the executable's dynamic section. The linux linker
 286  * wrote the address of its r_debug structure to the DT_DEBUG dynamic entry. We
 287  * get the address of the executable's program headers from the
 288  * AT_SUN_BRAND_LX_PHDR aux vector entry. From there, we calculate the
 289  * address of the Elf header, and from there we can easily get to the DT_DEBUG
 290  * entry.
 291  */
 292 static rd_helper_data_t
 293 lx_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php)
 294 {
 295         lx_rd_t         *lx_rd;
 296         uint32_t        addr, phdr_addr, dyn_addr;
 297         Elf32_Dyn       *dyn;
 298         Elf32_Phdr      phdr, *ph, *phdrs;
 299         Elf32_Ehdr      ehdr;
 300         int             i, dyn_count;
 301 
 302         lx_rd = calloc(sizeof (lx_rd_t), 1);
 303         if (lx_rd == NULL) {
 304                 ps_plog("lx_ldb_init: cannot allocate memory");
 305                 return (NULL);
 306         }
 307         lx_rd->lr_rap = rap;
 308         lx_rd->lr_php = php;
 309 
 310         phdr_addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_PHDR);
 311         if (phdr_addr == (uint32_t)-1) {
 312                 ps_plog("lx_ldb_init: no LX_PHDR found in aux vector");
 313                 return (NULL);
 314         }
 315         ps_plog("lx_ldb_init: found LX_PHDR auxv phdr at: 0x%p",
 316             phdr_addr);
 317 
 318         if (ps_pread(php, phdr_addr, &phdr, sizeof (phdr)) != PS_OK) {
 319                 ps_plog("lx_ldb_init: couldn't read phdr at 0x%p",
 320                     phdr_addr);
 321                 free(lx_rd);
 322                 return (NULL);
 323         }
 324 
 325         /* The ELF headher should be before the program header in memory */
 326         lx_rd->lr_exec = addr = phdr_addr - phdr.p_offset;
 327         if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
 328                 ps_plog("lx_ldb_init: couldn't read ehdr at 0x%p",
 329                     lx_rd->lr_exec);
 330                 free(lx_rd);
 331                 return (NULL);
 332         }
 333         ps_plog("lx_ldb_init: read ehdr at: 0x%p", addr);
 334 
 335         if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) {
 336                 ps_plog("lx_ldb_init: couldn't alloc phdrs memory");
 337                 free(lx_rd);
 338                 return (NULL);
 339         }
 340 
 341         if (ps_pread(php, phdr_addr, phdrs, ehdr.e_phnum * ehdr.e_phentsize) !=
 342             PS_OK) {
 343                 ps_plog("lx_ldb_init: couldn't read phdrs at 0x%p",
 344                     phdr_addr);
 345                 free(lx_rd);
 346                 free(phdrs);
 347                 return (NULL);
 348         }
 349         ps_plog("lx_ldb_init: read %d phdrs at: 0x%p",
 350             ehdr.e_phnum, phdr_addr);
 351 
 352         for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
 353             /*LINTED */
 354             ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
 355                 if (ph->p_type == PT_DYNAMIC)
 356                         break;
 357         }
 358         if (i == ehdr.e_phnum) {
 359                 ps_plog("lx_ldb_init: no PT_DYNAMIC in executable");
 360                 free(lx_rd);
 361                 free(phdrs);
 362                 return (NULL);
 363         }
 364         ps_plog("lx_ldb_init: found PT_DYNAMIC phdr[%d] at: 0x%p",
 365             i, (phdr_addr + ((char *)ph - (char *)phdrs)));
 366 
 367         if ((dyn = malloc(ph->p_filesz)) == NULL) {
 368                 ps_plog("lx_ldb_init: couldn't alloc for PT_DYNAMIC");
 369                 free(lx_rd);
 370                 free(phdrs);
 371                 return (NULL);
 372         }
 373 
 374         dyn_addr = addr + ph->p_offset;
 375         dyn_count = ph->p_filesz / sizeof (Elf32_Dyn);
 376         if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
 377                 ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p",
 378                     dyn_addr);
 379                 free(lx_rd);
 380                 free(phdrs);
 381                 free(dyn);
 382                 return (NULL);
 383         }
 384         ps_plog("lx_ldb_init: read %d dynamic headers at: 0x%p",
 385             dyn_count, dyn_addr);
 386 
 387         for (i = 0; i < dyn_count; i++) {
 388                 if (dyn[i].d_tag == DT_DEBUG) {
 389                         lx_rd->lr_rdebug = dyn[i].d_un.d_ptr;
 390                         break;
 391                 }
 392         }
 393         free(phdrs);
 394         free(dyn);
 395 
 396         if (lx_rd->lr_rdebug == 0) {
 397                 ps_plog("lx_ldb_init: no DT_DEBUG found in exe");
 398                 free(lx_rd);
 399                 return (NULL);
 400         }
 401         ps_plog("lx_ldb_init: found DT_DEBUG: 0x%p", lx_rd->lr_rdebug);
 402 
 403         return ((rd_helper_data_t)lx_rd);
 404 }
 405 
 406 /*
 407  * Given the address of an ELF object in the target, return its size and
 408  * the proper link map ID.
 409  */
 410 static size_t
 411 lx_elf_props32(struct ps_prochandle *php, uint32_t addr, psaddr_t *data_addr)
 412 {
 413         Elf32_Ehdr      ehdr;
 414         Elf32_Phdr      *phdrs, *ph;
 415         int             i;
 416         uint32_t        min = (uint32_t)-1;
 417         uint32_t        max = 0;
 418         size_t          sz = NULL;
 419 
 420         if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
 421                 ps_plog("lx_elf_props: Couldn't read ELF header at 0x%p",
 422                     addr);
 423                 return (0);
 424         }
 425 
 426         if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL)
 427                 return (0);
 428 
 429         if (ps_pread(php, addr + ehdr.e_phoff, phdrs, ehdr.e_phnum *
 430             ehdr.e_phentsize) != PS_OK) {
 431                 ps_plog("lx_elf_props: Couldn't read program headers at 0x%p",
 432                     addr + ehdr.e_phoff);
 433                 return (0);
 434         }
 435 
 436         for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
 437             /*LINTED */
 438             ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
 439 
 440                 if (ph->p_type != PT_LOAD)
 441                         continue;
 442 
 443                 if ((ph->p_flags & (PF_W | PF_R)) == (PF_W | PF_R)) {
 444                         *data_addr = ph->p_vaddr;
 445                         if (ehdr.e_type == ET_DYN)
 446                                 *data_addr += addr;
 447                         if (*data_addr & (ph->p_align - 1))
 448                                 *data_addr = *data_addr & (~(ph->p_align -1));
 449                 }
 450 
 451                 if (ph->p_vaddr < min)
 452                         min = ph->p_vaddr;
 453 
 454                 if (ph->p_vaddr > max) {
 455                         max = ph->p_vaddr;
 456                         sz = ph->p_memsz + max - min;
 457                         if (sz & (ph->p_align - 1))
 458                                 sz = (sz & (~(ph->p_align - 1))) + ph->p_align;
 459                 }
 460         }
 461 
 462         free(phdrs);
 463         return (sz);
 464 }
 465 
 466 static int
 467 lx_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data)
 468 {
 469         lx_rd_t                 *lx_rd = (lx_rd_t *)rhd;
 470         struct ps_prochandle    *php = lx_rd->lr_php;
 471         lx_r_debug_t            r_debug;
 472         lx_link_map_t           map;
 473         uint32_t                p = NULL;
 474         int                     rc;
 475         rd_loadobj_t            exec;
 476 
 477         if ((rc = ps_pread(php, (psaddr_t)lx_rd->lr_rdebug, &r_debug,
 478             sizeof (r_debug))) != PS_OK) {
 479                 ps_plog("lx_ldb_loadobj_iter: "
 480                     "Couldn't read linux r_debug at 0x%p", lx_rd->lr_rdebug);
 481                 return (rc);
 482         }
 483 
 484         p = r_debug.r_map;
 485 
 486         /*
 487          * The first item on the link map list is for the executable, but it
 488          * doesn't give us any useful information about it. We need to
 489          * synthesize a rd_loadobj_t for the client.
 490          *
 491          * Linux doesn't give us the executable name, so we'll get it from
 492          * the AT_EXECNAME entry instead.
 493          */
 494         if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) != PS_OK) {
 495                 ps_plog("lx_ldb_loadobj_iter: "
 496                     "Couldn't read linux link map at 0x%p", p);
 497                 return (rc);
 498         }
 499 
 500         bzero(&exec, sizeof (exec));
 501         exec.rl_base = lx_rd->lr_exec;
 502         exec.rl_dynamic = map.lxm_ld;
 503         exec.rl_nameaddr = lx_ldb_getauxval32(php, AT_SUN_EXECNAME);
 504         exec.rl_lmident = LM_ID_BASE;
 505 
 506         exec.rl_bend = exec.rl_base +
 507             lx_elf_props32(php, lx_rd->lr_exec, &exec.rl_data_base);
 508 
 509         if ((*cb)(&exec, client_data) == 0) {
 510                 ps_plog("lx_ldb_loadobj_iter: "
 511                     "client callb failed for executable");
 512                 return (PS_ERR);
 513         }
 514 
 515         for (p = map.lxm_next; p != NULL; p = map.lxm_next) {
 516                 rd_loadobj_t    obj;
 517 
 518                 if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) !=
 519                     PS_OK) {
 520                         ps_plog("lx_ldb_loadobj_iter: "
 521                             "Couldn't read lk map at %p", p);
 522                         return (rc);
 523                 }
 524 
 525                 /*
 526                  * The linux link map has less information than the Solaris one.
 527                  * We need to go fetch the missing information from the ELF
 528                  * headers.
 529                  */
 530 
 531                 obj.rl_nameaddr = (psaddr_t)map.lxm_name;
 532                 obj.rl_base = map.lxm_addr;
 533                 obj.rl_refnameaddr = (psaddr_t)map.lxm_name;
 534                 obj.rl_plt_base = NULL;
 535                 obj.rl_plt_size = 0;
 536                 obj.rl_lmident = LM_ID_BASE;
 537 
 538                 /*
 539                  * Ugh - we have to walk the ELF stuff, find the PT_LOAD
 540                  * sections, and calculate the end of the file's mappings
 541                  * ourselves.
 542                  */
 543 
 544                 obj.rl_bend = map.lxm_addr +
 545                     lx_elf_props32(php, map.lxm_addr, &obj.rl_data_base);
 546                 obj.rl_padstart = obj.rl_base;
 547                 obj.rl_padend = obj.rl_bend;
 548                 obj.rl_dynamic = map.lxm_ld;
 549                 obj.rl_tlsmodid = 0;
 550 
 551                 ps_plog("lx_ldb_loadobj_iter: 0x%p to 0x%p",
 552                     obj.rl_base, obj.rl_bend);
 553 
 554                 if ((*cb)(&obj, client_data) == 0) {
 555                         ps_plog("lx_ldb_loadobj_iter: "
 556                             "Client callback failed on %s", map.lxm_name);
 557                         return (rc);
 558                 }
 559         }
 560         return (RD_OK);
 561 }
 562 
 563 /*
 564  * Librtld_db plugin linkage struct.
 565  *
 566  * When we get loaded by librtld_db, it will look for the symbol below
 567  * to find our plugin entry points.
 568  */
 569 rd_helper_ops_t RTLD_DB_BRAND_OPS = {
 570         LM_ID_BRAND,
 571         lx_ldb_init32,
 572         lx_ldb_fini32,
 573         lx_ldb_loadobj_iter32,
 574         lx_ldb_get_dyns32
 575 };